我们可以使用泛型参数动态调用Java接口方法吗?

时间:2013-05-20 19:47:25

标签: java generics

以下代码编译并正常工作。我定义了一个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的实施者“关闭”特定的产品类型(即CarBus,而不是基类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泛型调用是一个编译时决定。有没有一种技术可以解决这个问题?

3 个答案:

答案 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来处理其任何组合。 (我为此编写了一些示例代码,如果您需要它,请随时给我发消息,我会发给您一份副本)