简体   繁体   English

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

[英]Can we dynamically invoke a Java interface method with a generic parameter?

The following code compiles and works fine. 以下代码编译并正常工作。 I defined a Builder interface, then the CarBuilder class is used to handle anything related to a Car, and the BusBuilder class is used to handle anything related to a Bus. 我定义了一个Builder接口,然后CarBuilder类用于处理与Car相关的任何事情,而BusBuilder类用于处理与总线相关的任何事情。 Car and Bus share an abstract class named Vehicle. Car和Bus共享一个名为Vehicle的抽象类。 The code is straightforward. 代码很简单。 The code will output: 代码将输出:

do something to CREATE the Car
do something to UPDATE the Car
do something to CREATE the Bus
do something to UPDATE the Bus

Here is the original code that compiles: 这是编译的原始代码:

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);
}

But, I'd like to make my code even more strongly-typed. 但是,我想让我的代码更加强烈。 I want to change the interface method, 我想改变界面方法,

from

public void updateVehicle(String spec, Vehicle vehicle);

to

public void updateVehicle(String spec, V vehicle);

In other words, I tried to use generic V instead of base class Vehicle in my interface signature. 换句话说,我试图在我的界面签名中使用泛型V而不是基类Vehicle This forces the implementors of Builder to "close" on the specific product type (ie either Car or Bus , but not the base class Vehicle ). 这迫使Builder的实现Builder “关闭”特定的产品类型(即CarBus ,而不是基类Vehicle )。 CarBuilder shall only handle a Car ; CarBuilder只能处理一辆Car ; BusBuilder should only handle a Bus . BusBuilder应该只处理一个Bus

The code becomes as follows: 代码如下:

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
}

Two compilation errors - Java compiler won't allow this because " The method updateVehicle(String, capture#3-of ? extends Vehicle) in the type Builder is not applicable for the arguments (String, Vehicle) ". 两个编译错误 - Java编译器不允许这样做,因为“ 类型Builder中的方法updateVehicle(String,capture#3-of?extends Vehicle)不适用于参数(String,Vehicle) ”。

In Java is there a way to do what I want to accomplish? 在Java中有没有办法做我想要完成的事情? In C# I can simply type my variable with var keyword, like var vehicle instead of Vehicle vehicle . 在C#中我可以简单地用var关键字输入我的变量,比如var vehicle而不是Vehicle vehicle I heard that Java generics invocation is a compile-time decision. 我听说Java泛型调用是一个编译时决定。 Is there a technique to overcome this? 有没有一种技术可以克服这个问题?

There is an idiom, called a capture helper, that helps in cases like this. 有一个成语,称为捕获助手,在这种情况下有所帮助。

Essentially, given your declaration of builder , all the compiler knows is that vehicle is something that extends Vehicle . 基本上,鉴于您的builder声明,所有编译器都知道vehicle是扩展Vehicle东西。 Further, it knows that Builder requires some specific type of Vehicle passed to its updateVehicle() method. 此外,它知道Builder需要将一些特定类型的Vehicle传递给其updateVehicle()方法。 The compiler is unable to draw the inference that is obvious to us—that the type is compatible. 编译器无法绘制对我们来说很明显的推理 - 该类型是兼容的。 To do that, we have to get rid of the wildcard by using a helper method. 要做到这一点,我们必须使用辅助方法摆脱通配符。

A practical application to real code is not obvious from your simple test harness. 从简单的测试工具中,实际代码的实际应用并不明显。 But, applying it in this context would look something like the following. 但是,在这种情况下应用它看起来像下面这样。 Hopefully this example will be enough to illustrate the idea and help you apply it to real code. 希望这个例子足以说明这个想法,并帮助你将它应用到真正的代码中。

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;
}

What you're trying to do doesn't make any sense : you want your builders to only accept a Car or a Bus (which is fine), but then you give them an instance of Vehicle. 你想要做的事情没有任何意义:你希望你的建造者只接受汽车或公共汽车(这很好),但是你给他们一个车辆的实例。 You're doing the exact thing you're trying to prevent : accept the generic class Vehicle. 你正在做你想要阻止的确切事情:接受通用类Vehicle。

Simplest method would be to filter input that doesn't match expected or desired input. 最简单的方法是过滤与预期或所需输入不匹配的输入。

if(vehicle instanceof Car) return;

A more thorough (and less brittle) implementation can be created by using enums to control various aspects of the car (Type, Color). 通过使用枚举来控制汽车的各个方面(类型,颜色),可以创建更彻底(且更不易碎)的实现。 You can then use a single Builder with switches to handle any combination thereof. 然后,您可以使用带有开关的单个Builder来处理其任何组合。 (I have some example code written up for this, if you need it then feel free to shoot me a message and I will send you a copy) (我有一些示例代码为此编写,如果你需要它然后随时给我发一条消息,我会发给你一份副本)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 具有函数式接口参数类型的 Java 泛型方法 - Java Generic on method with Functional Interface parameter type 为什么Java泛型类可以使用Object参数实现泛型接口的方法? - Why can a Java generic class implement a method of a generic interface with Object parameter? 如何使用Java反射调用以可调用接口或方法作为参数的方法 - How to Invoke a method that take a Callable Interface or method as parameter with java reflection Java参数通用接口 - Java Parameter Generic interface JAVA:调用期望另一个接口作为参数的 AIDL 服务方法 - JAVA: Invoke an AIDL Service method that expects another Interface as a parameter Java 使用泛型参数和默认方法实现编译功能接口失败 - Java failed to compile Functional Interface with generic parameter and default method implementation Java static 实现带有泛型方法参数接口的类的工厂 - Java static factory for classes that implement interface with generic method parameter Java泛型:方法级别与接口级别定义的泛型参数 - Java generics: Generic parameter defined at method level vs interface level 为什么接口的泛型方法可以在Java中实现为非泛型? - Why a generic method of an interface can be implemented as non-generic in Java? 为什么我们可以调用来自 Java 中另一个线程的对象的方法? - WHY can we invoke a method of an object that is FROM another thread in Java?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM