我的应用程序有一个简单的插件API。它打包在一个单独的JAR中,插件开发人员可以使用它。核心应用程序实现API中包含的接口,并将它们公开给加载了ServiceLoader
的插件。
考虑以下代码:
public interface ILayer {}
public class Layer implements ILayer {
public void internalMethod() { /*snip*/ }
}
public interface IPlotter {
List<ILayer> getLayers();
}
public class Plotter implements IPlotter {
private ObservableList<Layer> layers = FXCollections.observableArrayList();
@Override
public ObservableList<Layer> getLayers() { // incompatible returned type
return layers;
}
}
我的要求是:
ObservableList
的{{1}}可用Layer
个List
不幸的是,将ILayer
投射到ObservableList<Layer>
是非法的。
在API中返回List<ILayer>
会公开List<Layer>
的{{1}},所以这没有用。
我也可以拥有internalMethod()
,然后实际存储Layer
,但每次使用private ObservableList<ILayer> layers
时都需要投放项目,我对这个想法并不是很热心
这个问题有一个干净的解决方案吗?
答案 0 :(得分:5)
一种选择是更改接口方法的签名以允许返回ILayer的子类型:
interface IPlotter {
List<? extends ILayer> getLayers();
}
class Plotter implements IPlotter {
private ObservableList<Layer> layers = FXCollections.observableArrayList();
@Override
public List<? extends ILayer> getLayers() {
return layers;
}
}
主要缺点是有人调用List<? extends ILayer> layers = plotter.getLayers();
将无法添加到列表中:layers.add(someLayer);
将无法编译。这可能是也可能不是问题。
答案 1 :(得分:2)
@ assylias的答案提供了该问题最简洁的解决方案。事实是List<Layer>
没有问题不是 List<ILayer>
(考虑:您只能添加ILayer
而不是Layer
后者),Java不接受它识别出来的强制转换。
但是,如果你肯定想要准确地返回List<ILayer>
,那么你至少有三个不错的选择:
List<ILayer>
,并在Plotter.getLayers()
内使用必要的转换。因为你(非常合理地)不喜欢演员,你也可以在内部使用List<Layer>
,然后创建 new List<Ilayer>
以从该方法返回,例如
return new ArrayList<ILayer>(layers);
作为(2)的变体,您可以创建一个包装列表而不是副本,假设返回的列表可以不可修改:
return Collections.<ILayer>unmodifiableList(layers);
请注意使用显式类型参数为您提供所需的结果类型参数化。但是,请注意,与assylias的List<? extends ILayer>
方法相比,此替代方案对使用返回值的限制更多。
答案 2 :(得分:1)
另一个解决方案可能是使用bindContent实用程序方法:
public class Plotter implements IPlotter {
private final ObservableList<Layer> layers = FXCollections.observableArrayList();
private final List<ILayer> ilayers = new ArrayList<>;
public Plotter(){
Bindings.bindContent(ilayers , layers);
}
@Override
public List<ILayer> getLayers() {
return ilayers;
}
}
它的缺点是你不应该操纵getLayers返回的List。来自Javadoc:
一旦List绑定到ObservableList,List就不能 直接改变了。这样做会导致意想不到的结果。
答案 3 :(得分:0)
如果适用,您可以尝试将internalMethod
移至ILayer
。或者尽可能将其删除。如果这些都不可能,您可以尝试使IPlotter
通用。然后,IPlotter<Layer>
是另一种类型IPlotter<Layer2>
。
interface ILayer {
}
class Layer implements ILayer {
public void internalMethod() { /*snip*/ }
}
class Layer2 implements ILayer {
public void internalMethod() { /*snip*/ }
}
interface IPlotter<T extends ILayer> {
List<T> getLayers();
}
class Plotter implements IPlotter<Layer> {
private ObservableList<Layer> layers = FXCollections.observableArrayList();
@Override
public ObservableList<Layer> getLayers() { // incompatible returned type
return layers;
}
}
class Plotter2 implements IPlotter<Layer2> {
private ObservableList<Layer2> layers = FXCollections.observableArrayList();
@Override
public ObservableList<Layer2> getLayers() { // incompatible returned type
return layers;
}
}
如果您有不同图层的多个绘图仪,则必须使用
List<IPlotter<? extends ILayer>> i = Arrays.asList(new Plotter(), new Plotter2());
答案 4 :(得分:-1)
怎么样?
private ObservableList<? extends ILayer> layers = FXCollections.observableArrayList();
?