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

我们能用泛型参数动态调用Java接口方法吗?,java,generics,Java,Generics,下面的代码编译后运行良好。我定义了一个构建器接口,然后CarBuilder类用于处理与汽车相关的任何内容,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 以下是编译的原始

下面的代码编译后运行良好。我定义了一个构建器接口,然后CarBuilder类用于处理与汽车相关的任何内容,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);
}

换句话说,我尝试在接口签名中使用generic
V
而不是基类
Vehicle
。这迫使
Builder
的实施者“关闭”特定产品类型(即
Car
Bus
,但不关闭基类
Vehicle
CarBuilder
只能处理
车辆
<代码>总线生成器应仅处理
总线

代码如下所示:

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
}
公共界面生成器{
公共车辆(字符串规范);
公共无效更新车辆(字符串规范,V车辆);
}
公共类CarBuilder实现生成器{
公共车辆(字符串规范){
汽车=新车();
System.out.println(“做点什么来创造汽车”);
返回车;
}
公共无效更新车辆(字符串规范,车辆){
System.out.println(“做点什么来更新汽车”);
返回;
}
}
公共类BusBuilder实现了BusBuilder{
公交车辆(字符串规范){
总线=新总线();
System.out.println(“做一些事情来创建总线”);
回程巴士;
}
公共无效更新工具(字符串规范,总线){
System.out.println(“做点什么来更新总线”);
返回;
}
}
@试验
公共图书馆{

建筑商你想做的没有任何意义:你希望你的建筑商只接受一辆汽车或一辆公共汽车(这很好),但你给他们一个Vehicle实例。你正在做你正试图阻止的事情:接受泛型类Vehicle。

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

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

有一个成语,叫做“在这种情况下很有用”

本质上,考虑到您对
builder
的声明,编译器只知道
vehicle
是对
vehicle
的扩展。此外,它还知道
builder
需要将某种特定类型的
vehicle
传递给它的
updatehicle()
method。编译器无法得出明显的推断,即类型是兼容的。为此,我们必须使用helper方法来消除通配符

从您的简单测试工具来看,实际代码的实际应用并不明显。但是,在这个上下文中应用它将类似于以下内容。希望这个示例足以说明这个想法,并帮助您将其应用到实际代码中

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;
}
public void main()
{

Builder为什么更新公共汽车或汽车的方法不在相应的类中?是的。Builder最常用于创建实例,而不是更新它。我必须用
Vehicle
键入变量,因为在Java中没有其他方法可以这样做。就像我在C中提到的那样,我可以用
var键入它保留运行时类型-我的变量
vehicle
在运行时可以容纳一辆汽车或一辆公共汽车。我想这可以归结为一个事实,Java泛型调用决策是在编译时做出的,因此没有其他工具可以绕过它。考虑到您正在使用新的CarBuilder()在第i行,然后调用它在第i+1行创建一个车辆,您可以安全地将类型Car提供给新创建的车辆。在我的实际项目中,生成器实例被注入,因此在我调用create()后,我无法将其键入Car或Bus。这样做是可行的,但他的目的是拥有一个强类型代码,而不是在运行时接收失败(我不会返回,我会抛出一个IllegalArgumentException或类似的东西)。说得好vtheron。谢谢你的回答。@vtheron我完全同意,但如果简单是一个目标,那么这是解决潜在问题的最简单的方法。给我一点时间,我现在正在写处理这个问题的最好方法(在我看来)。
if(vehicle instanceof Car) return;
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;
}