我们能用泛型参数动态调用Java接口方法吗?
下面的代码编译后运行良好。我定义了一个构建器接口,然后CarBuilder类用于处理与汽车相关的任何内容,BusBuilder类用于处理与总线相关的任何内容。Car和Bus共享一个名为Vehicle的抽象类。代码很简单。代码将输出:我们能用泛型参数动态调用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 以下是编译的原始
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);
}
到
换句话说,我尝试在接口签名中使用genericV
而不是基类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;
}