[英]Can we dynamically invoke a Java interface method with a generic parameter?
The following code compiles and works fine. 以下代码编译并正常工作。 I defined a Builder interface, then the CarBuilder class is used to handle anything related to a Car, and the BusBuilder class is used to handle anything related to a Bus. 我定义了一个Builder接口,然后CarBuilder类用于处理与Car相关的任何事情,而BusBuilder类用于处理与总线相关的任何事情。 Car and Bus share an abstract class named Vehicle. Car和Bus共享一个名为Vehicle的抽象类。 The code is straightforward. 代码很简单。 The code will output: 代码将输出:
do something to CREATE the Car
do something to UPDATE the Car
do something to CREATE the Bus
do something to UPDATE the Bus
Here is the original code that compiles: 这是编译的原始代码:
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);
}
But, I'd like to make my code even more strongly-typed. 但是,我想让我的代码更加强烈。 I want to change the interface method, 我想改变界面方法,
from 从
public void updateVehicle(String spec, Vehicle vehicle);
to 至
public void updateVehicle(String spec, V vehicle);
In other words, I tried to use generic V
instead of base class Vehicle
in my interface signature. 换句话说,我试图在我的界面签名中使用泛型V
而不是基类Vehicle
。 This forces the implementors of Builder
to "close" on the specific product type (ie either Car
or Bus
, but not the base class Vehicle
). 这迫使Builder
的实现Builder
“关闭”特定的产品类型(即Car
或Bus
,而不是基类Vehicle
)。 CarBuilder
shall only handle a Car
; CarBuilder
只能处理一辆Car
; BusBuilder
should only handle a Bus
. BusBuilder
应该只处理一个Bus
。
The code becomes as follows: 代码如下:
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
}
Two compilation errors - Java compiler won't allow this because " The method updateVehicle(String, capture#3-of ? extends Vehicle) in the type Builder is not applicable for the arguments (String, Vehicle) ". 两个编译错误 - Java编译器不允许这样做,因为“ 类型Builder中的方法updateVehicle(String,capture#3-of?extends Vehicle)不适用于参数(String,Vehicle) ”。
In Java is there a way to do what I want to accomplish? 在Java中有没有办法做我想要完成的事情? In C# I can simply type my variable with var keyword, like var vehicle
instead of Vehicle vehicle
. 在C#中我可以简单地用var关键字输入我的变量,比如var vehicle
而不是Vehicle vehicle
。 I heard that Java generics invocation is a compile-time decision. 我听说Java泛型调用是一个编译时决定。 Is there a technique to overcome this? 有没有一种技术可以克服这个问题?
There is an idiom, called a capture helper, that helps in cases like this. 有一个成语,称为捕获助手,在这种情况下有所帮助。
Essentially, given your declaration of builder
, all the compiler knows is that vehicle
is something that extends Vehicle
. 基本上,鉴于您的builder
声明,所有编译器都知道vehicle
是扩展Vehicle
东西。 Further, it knows that Builder
requires some specific type of Vehicle
passed to its updateVehicle()
method. 此外,它知道Builder
需要将一些特定类型的Vehicle
传递给其updateVehicle()
方法。 The compiler is unable to draw the inference that is obvious to us—that the type is compatible. 编译器无法绘制对我们来说很明显的推理 - 该类型是兼容的。 To do that, we have to get rid of the wildcard by using a helper method. 要做到这一点,我们必须使用辅助方法摆脱通配符。
A practical application to real code is not obvious from your simple test harness. 从简单的测试工具中,实际代码的实际应用并不明显。 But, applying it in this context would look something like the following. 但是,在这种情况下应用它看起来像下面这样。 Hopefully this example will be enough to illustrate the idea and help you apply it to real code. 希望这个例子足以说明这个想法,并帮助你将它应用到真正的代码中。
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;
}
What you're trying to do doesn't make any sense : you want your builders to only accept a Car or a Bus (which is fine), but then you give them an instance of Vehicle. 你想要做的事情没有任何意义:你希望你的建造者只接受汽车或公共汽车(这很好),但是你给他们一个车辆的实例。 You're doing the exact thing you're trying to prevent : accept the generic class Vehicle. 你正在做你想要阻止的确切事情:接受通用类Vehicle。
Simplest method would be to filter input that doesn't match expected or desired input. 最简单的方法是过滤与预期或所需输入不匹配的输入。
if(vehicle instanceof Car) return;
A more thorough (and less brittle) implementation can be created by using enums to control various aspects of the car (Type, Color). 通过使用枚举来控制汽车的各个方面(类型,颜色),可以创建更彻底(且更不易碎)的实现。 You can then use a single Builder with switches to handle any combination thereof. 然后,您可以使用带有开关的单个Builder来处理其任何组合。 (I have some example code written up for this, if you need it then feel free to shoot me a message and I will send you a copy) (我有一些示例代码为此编写,如果你需要它然后随时给我发一条消息,我会发给你一份副本)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.