以下代码编译并正常工作。我定义了一个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
的实施者“关闭”特定的产品类型(即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泛型调用是一个编译时决定。有没有一种技术可以解决这个问题?
答案 0 :(得分:4)
有一个成语,称为a capture helper,,可以帮助这样的情况。
基本上,鉴于你的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;
}
答案 1 :(得分:2)
你想要做的事情没有任何意义:你希望你的建造者只接受汽车或公共汽车(这很好),但是你给他们一个车辆的实例。你正在做你想要阻止的确切事情:接受通用类Vehicle。
答案 2 :(得分:0)
最简单的方法是过滤与预期或所需输入不匹配的输入。
if(vehicle instanceof Car) return;
通过使用枚举来控制汽车的各个方面(类型,颜色),可以创建更彻底(且更不易碎)的实现。然后,您可以使用带有开关的单个Builder来处理其任何组合。 (我为此编写了一些示例代码,如果您需要它,请随时给我发消息,我会发给您一份副本)