清洁解决方案“将”ObservableList <foo>“强制转换为List <ifoo>?

时间:2015-12-02 15:01:38

标签: java generics javafx interface casting

我的应用程序有一个简单的插件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}}可用
  • API仅公开LayerList

不幸的是,将ILayer投射到ObservableList<Layer>是非法的。

在API中返回List<ILayer>会公开List<Layer>的{​​{1}},所以这没有用。

我也可以拥有internalMethod(),然后实际存储Layer,但每次使用private ObservableList<ILayer> layers时都需要投放项目,我对这个想法并不是很热心

这个问题有一个干净的解决方案吗?

5 个答案:

答案 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>,那么你至少有三个不错的选择:

  1. 首先创建一个List<ILayer>,并在Plotter.getLayers()内使用必要的转换。因为你(非常合理地)不喜欢演员,你也可以
  2. 在内部使用List<Layer>,然后创建 new List<Ilayer>以从该方法返回,例如

    return new ArrayList<ILayer>(layers);
    
  3. 作为(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();