Spigot插件开发:如何将Shape实现类类型作为Selection构造方法参数?
嘿,这个问题我熟!你的思路其实是对的,用Class<? extends Shape>作为构造参数完全可行,不过具体实现的时候要注意反射的细节,另外还有更类型安全的替代方案,我给你一步步讲清楚:
方案一:使用Class类型参数(反射方式)
这种方式和你最初的想法一致,通过传入Shape实现类的Class对象,在Selection构造方法里利用反射创建对应的形状实例。
首先是Selection类的实现:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Selection { private final int x; private final int z; private final int width; private final int depth; private final Shape shapeInstance; // 构造方法接收形状类型和区域参数 public Selection(Class<? extends Shape> shapeType, int x, int z, int width, int depth) { this.x = x; this.z = z; this.width = width; this.depth = depth; try { // 假设你的Shape实现类都有接收x,z,width,depth的构造方法 Constructor<? extends Shape> constructor = shapeType.getConstructor(int.class, int.class, int.class, int.class); this.shapeInstance = constructor.newInstance(x, z, width, depth); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("形状类 " + shapeType.getName() + " 没有匹配的构造方法", e); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("创建形状实例失败", e); } } // 后续可以直接使用shapeInstance进行操作 public void fillRegion() { // 调用Shape接口定义的方法,比如shapeInstance.fillBlocks(); } }
调用的时候就像这样:
// 创建矩形区域的Selection Selection rectangleSelection = new Selection(Rectangle.class, 100, 200, 50, 30); // 创建椭圆区域的Selection Selection ellipseSelection = new Selection(Ellipse.class, 150, 250, 40, 60);
注意事项:
- 必须保证所有Shape的实现类都有public的、参数为
int, int, int, int的构造方法,否则反射会抛出异常。 - 反射虽然灵活,但编译时无法检查构造方法是否存在,所以如果后续修改了Shape实现类的构造,需要同步更新Selection的反射逻辑。
方案二:使用Supplier(类型安全方案)
如果你想避免反射的潜在问题,更推荐用Supplier<Shape>或者函数式接口来传递形状的创建逻辑,这样编译时就能检查错误,类型更安全。
修改后的Selection类:
import java.util.function.Supplier; public class Selection { private final int x; private final int z; private final int width; private final int depth; private final Shape shapeInstance; // 构造方法接收Supplier,让调用者负责创建形状实例 public Selection(int x, int z, int width, int depth, Supplier<Shape> shapeSupplier) { this.x = x; this.z = z; this.width = width; this.depth = depth; this.shapeInstance = shapeSupplier.get(); } }
调用的时候可以用Lambda表达式或者方法引用,非常灵活:
// 用Lambda创建矩形 Selection rectangleSelection = new Selection(100, 200, 50, 30, () -> new Rectangle(100, 200, 50, 30)); // 椭圆同理 Selection ellipseSelection = new Selection(150, 250, 40, 60, () -> new Ellipse(150, 250, 40, 60));
优势:
- 编译时就能检查形状实例的创建是否正确,不会出现反射的运行时异常。
- 更灵活:如果某个Shape实现类需要特殊的构造参数,调用者可以自行处理,不需要修改Selection类的逻辑。
额外建议
如果你的Selection后续需要多次创建形状实例(而不是只创建一次),可以把Supplier<Shape>或者Class<? extends Shape>保存为成员变量,每次需要的时候再创建实例,而不是在构造方法里只创建一次。
比如修改方案一,保存Class对象:
public class Selection { private final Class<? extends Shape> shapeType; private final int x; private final int z; private final int width; private final int depth; public Selection(Class<? extends Shape> shapeType, int x, int z, int width, int depth) { this.shapeType = shapeType; this.x = x; this.z = z; this.width = width; this.depth = depth; } // 每次需要时创建实例 public Shape createShapeInstance() { try { Constructor<? extends Shape> constructor = shapeType.getConstructor(int.class, int.class, int.class, int.class); return constructor.newInstance(x, z, width, depth); } catch (Exception e) { throw new RuntimeException("创建形状失败", e); } } }
这样就可以根据需要多次创建形状对象啦!
内容的提问来源于stack exchange,提问作者DddoubleChin
相关产品推荐
相关产品推荐

