[英]Can we dynamically invoke a Java interface method with a generic parameter?
以下代码编译并正常工作。 我定义了一个Builder接口,然后CarBuilder类用于处理与Car相关的任何事情,而BusBuilder类用于处理与总线相关的任何事情。 Car和Bus共享一个名为Vehicle的抽象类。 代码很简单。 代码将输出:
do something to CREATE the Car
do something to UPDATE the Car
do something to CREATE the Bus
do something to UPDATE the Bus
这是编译的原始代码:
public abstract class Vehicle { }
public class Car extends Vehicle { }
public class Bus extends Vehicle { }
public interface Builder<V extends Vehicle> {
public V createVehicle(String spec);
public void updateVehicle(String spec, Vehicle vehicle);
}
public class CarBuilder implements Builder<Car> {
public Car createVehicle(String spec) {
Car car = new Car();
System.out.println("do something to CREATE the Car");
return car;
}
public void updateVehicle(String spec, Vehicle vehicle) {
Car car = (Car) vehicle;
System.out.println("do something to UPDATE the Car");
return;
}
}
public class BusBuilder implements Builder<Bus> {
public Bus createVehicle(String spec) {
Bus bus = new Bus();
System.out.println("do something to CREATE the Bus");
return bus;
}
public void updateVehicle(String spec, Vehicle vehicle) {
Bus bus = (Bus) vehicle;
System.out.println("do something to UPDATE the Bus");
return;
}
}
@Test
public void main() {
Builder<? extends Vehicle> builder = null;
Vehicle vehicle = null;
builder = new CarBuilder();
vehicle = builder.createVehicle("my original Car spec");
builder.updateVehicle("my modified Car spec", vehicle);
builder = new BusBuilder();
vehicle = builder.createVehicle("my original Bus spec");
builder.updateVehicle("my modified Bus spec", vehicle);
}
但是,我想让我的代码更加强烈。 我想改变界面方法,
从
public void updateVehicle(String spec, Vehicle vehicle);
至
public void updateVehicle(String spec, V vehicle);
换句话说,我试图在我的界面签名中使用泛型V
而不是基类Vehicle
。 这迫使Builder
的实现Builder
“关闭”特定的产品类型(即Car
或Bus
,而不是基类Vehicle
)。 CarBuilder
只能处理一辆Car
; BusBuilder
应该只处理一个Bus
。
代码如下:
public interface Builder<V extends Vehicle> {
public V createVehicle(String spec);
public void updateVehicle(String spec, V vehicle);
}
public class CarBuilder implements Builder<Car> {
public Car createVehicle(String spec) {
Car car = new Car();
System.out.println("do something to CREATE the Car");
return car;
}
public void updateVehicle(String spec, Car car) {
System.out.println("do something to UPDATE the Car");
return;
}
}
public class BusBuilder implements Builder<Bus> {
public Bus createVehicle(String spec) {
Bus bus = new Bus();
System.out.println("do something to CREATE the Bus");
return bus;
}
public void updateVehicle(String spec, Bus bus) {
System.out.println("do something to UPDATE the Bus");
return;
}
}
@Test
public void main() {
Builder<? extends Vehicle> builder = null;
Vehicle vehicle = null;
builder = new CarBuilder();
vehicle = builder.createVehicle("my original Car spec");
builder.updateVehicle("my modified Car spec", vehicle); <== compilation error
builder = new BusBuilder();
vehicle = builder.createVehicle("my original Bus spec");
builder.updateVehicle("my modified Bus spec", vehicle); <== compilation error
}
两个编译错误 - Java编译器不允许这样做,因为“ 类型Builder中的方法updateVehicle(String,capture#3-of?extends Vehicle)不适用于参数(String,Vehicle) ”。
在Java中有没有办法做我想要完成的事情? 在C#中我可以简单地用var关键字输入我的变量,比如var vehicle
而不是Vehicle vehicle
。 我听说Java泛型调用是一个编译时决定。 有没有一种技术可以克服这个问题?
有一个成语,称为捕获助手,在这种情况下有所帮助。
基本上,鉴于您的builder
声明,所有编译器都知道vehicle
是扩展Vehicle
东西。 此外,它知道Builder
需要将一些特定类型的Vehicle
传递给其updateVehicle()
方法。 编译器无法绘制对我们来说很明显的推理 - 该类型是兼容的。 要做到这一点,我们必须使用辅助方法摆脱通配符。
从简单的测试工具中,实际代码的实际应用并不明显。 但是,在这种情况下应用它看起来像下面这样。 希望这个例子足以说明这个想法,并帮助你将它应用到真正的代码中。
public void main()
{
Builder<? extends Vehicle> builder;
Vehicle vehicle;
builder = new CarBuilder();
vehicle = createAndUpdateHelper(builder, "my original Car spec", "my modified Car spec");
builder = new BusBuilder();
vehicle = createAndUpdateHelper(builder, "my original Bus spec", "my modified Bus spec");
}
private static <V extends Vehicle> V createAndUpdateHelper(Builder<V> builder, String s1, String s2)
{
V v = builder.createVehicle(s1);
builder.updateVehicle(s2, v);
return v;
}
你想要做的事情没有任何意义:你希望你的建造者只接受汽车或公共汽车(这很好),但是你给他们一个车辆的实例。 你正在做你想要阻止的确切事情:接受通用类Vehicle。
最简单的方法是过滤与预期或所需输入不匹配的输入。
if(vehicle instanceof Car) return;
通过使用枚举来控制汽车的各个方面(类型,颜色),可以创建更彻底(且更不易碎)的实现。 然后,您可以使用带有开关的单个Builder来处理其任何组合。 (我有一些示例代码为此编写,如果你需要它然后随时给我发一条消息,我会发给你一份副本)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.