繁体   English   中英

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

[英]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 “关闭”特定的产品类型(即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泛型调用是一个编译时决定。 有没有一种技术可以克服这个问题?

有一个成语,称为捕获助手,在这种情况下有所帮助。

基本上,鉴于您的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM