首页 > 解决方案 > 如何维护依赖于每个具体实现的异构类型的集合?

问题描述

尽管Java的类型擦除,我试图找到一种方法来维护多个集合中的异构类型。

具体来说,如果我有几个接口

public interface Fuel {};

public interface Engine<T extends Fuel> {
  public void burn(T fuel);
}

我希望能够将一组燃料和一组引擎放在一起。这样我就可以将每个发动机的燃烧称为正确的燃料类型。

class PowerPlant {

  List<Engine<?>> engines;
  List<Fuel> fuels;

  public void run() {
    fuels.stream().forEach(fuel -> {
        engines
          .filter(/* Not where I need help. */)
          .burn(fuel); // This cast is my problem
    });
  }
}

以上因有趣的类型不匹配而失败

The method burn(capture#24-of ?) in the type Engine<capture#24-of ?> is not applicable for the arguments (Fuel<capture#25-of ?>)

有没有办法恢复类型让编译器开心?

(这就是我的问题的结尾,下面的一切都是我尝试过的不同的东西。)


我发现实际编译的最糟糕的选择是拥有一个元容器。

public class FuelEngine<T extends Fuel, S extends Engine<T>> {
  public T fuel;
  public S engine;

  public void burn() {
    engine.burn(fuel);
  }
}

然后我可以将其构建到我的PowerPlant

public class PowerPlant {
  List<FuelEngine<?, ?>> fuelEngines;

  public void run() {
    fuelEngines.stream().forEach(FuelEngine::burn);
  }
}

但是我有更多的方法,而不仅仅是烧录(例如,,,burnFastburnSlowmixFuel,并且真的不想为每个引入内部的新方法复制粘贴一堆“虚拟”方法Engine


我尝试过的失败或同样没有编译或更糟的事情(IMO)。

  1. “动态”铸造
public interface Engine<T extends Fuel> {
  public void burn(T fuel);
  public Class<T> getFuelType();
}

接着

public class PowerPlant {
  List<FuelEngine<?, ?>> fuelEngines;

  public void run() {
    fuelEngines.stream().forEach(fuelEngine -> {
      Engine<?> engine = fuelEngine.getEngine();
      engine.burn(engine.getFuelType().cast(fuelEngine.getFuel()));
    });
  } 
}

给出错误

The method burn(capture#19-of ?) in the type Engine<capture#19-of ?> is not applicable for the arguments (capture#20-of ?)
  1. 有一个类型化的方法
public PowerPlant {
  List<FuelEngine<?, ?>> fuelEngines;

  public void run() {
    fuelEngines.stream().forEach(fuelEngine -> burn(fuelEngine.getFuel(), fuelEngine.getEngine());
  }

  public <T extends Fuel, S extends Engine<T>> void burn(T fuel, S engine) {
    engine.burn(fuel);
  }
}

给出错误

The method burn(T, S) in the type PowerPlant is not applicable for the arguments (capture#26-of ?, capture#28-of ?)
  1. 自定义异构类型安全容器
class FuelContainer {
  Map<Class<?>, List<? extends Fuel>> fuelByType;

  public <T extends Fuel> List<T> getFuels(Class<T> type) {
    return fuelByType.get(type).stream().map(type::cast).collect(Collectors.toList());
  }
}

随着PowerPlant实施

public class PowerPlant {
  public List<Engine<?>> engines;
  public FuelContainer fuelContainer;

  public void run() {
    engines.stream().forEach(engine -> {
      fuelContainer.getFuels(engine.getFuelType()).stream().forEach(engine::burn);
    });
  }
}

给出两个错误!

The method forEach(Consumer<? super capture#33-of ?>) in the type Iterable<capture#33-of ?> is not applicable for the arguments (engine::burn)
The type Engine<capture#34-of ?> does not define burn(capture#33-of ?) that is applicable here
  1. 接受EngineObject做自己的演员。
public interface Engine {
  public void burn(Object fuel); 
}

但这只是……丑陋,而且我的编译器失去了类型安全性。

  1. 有一堆类型的列表
public class PowerPlant {
  List<ElectricEngine> electricEngines;
  List<GasEngine> gasEngines;
  
  List<Electricity> electricies;
  List<GasFuel> gasFuels;
}

但是当我将拥有大约 30 种不同的发动机和燃料时,这并没有规模。

标签: javagenericstypes

解决方案


我完全忘记了原始类型是一回事!

我已经用我的过滤器验证了正确Fuel的发送到正确的位置。Engine在引擎列表上使用原始类型只会导致编译器发出警告(而不是错误)。那些可以忽略,现在一切都像我想要的那样工作!

@SuppressWarnings({ "rawtypes", "java:S3740", "unchecked" })
class PowerPlant {

  List<Engine> engines; // Warning raw type
  List<Fuel> fuels;

  public void run() {
    fuels.stream().forEach(fuel -> {
        engines
          .filter(/* snip */)
          .burn(fuel); // Warning type safety
    });
  }
}

朋友之间有几个@SuppressWarnings?


推荐阅读