5

以下代码编译并正常工作。我定义了一个 Builder 接口,然后 CarBuilder 类用于处理与 Car 相关的任何事情,而 BusBuilder 类用于处理与 Bus 相关的任何事情。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“关闭”特定的产品类型(即Caror Bus,但不是基类Vehicle)。 CarBuilder只应处理一个CarBusBuilder应该只处理一个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 泛型调用是编译时的决定。有什么技术可以克服吗?

4

3 回答 3

4

有一个习惯用法,称为捕获助手,可以在这种情况下提供帮助。

本质上,给定您的声明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;
}
于 2013-05-20T20:09:00.303 回答
2

您尝试做的事情没有任何意义:您希望您的构建者只接受 Car 或 Bus(这很好),但随后您给他们一个 Vehicle 的实例。你正在做你试图阻止的事情:接受泛型类 Vehicle。

于 2013-05-20T19:51:46.600 回答
0

最简单的方法是过滤与预期或期望输入不匹配的输入。

if(vehicle instanceof Car) return;

通过使用枚举来控制汽车的各个方面(类型、颜色),可以创建一个更彻底(并且不那么脆弱)的实现。然后,您可以使用带有开关的单个 Builder 来处理它们的任何组合。(我为此编写了一些示例代码,如果您需要它,请随时给我发消息,我会发给您一份)

于 2013-05-20T20:01:26.890 回答