2015-12-02 3 views
2

Мое приложение имеет простой API-интерфейс плагина. Он упакован в отдельный JAR, который разработчики плагина могут использовать. Основное приложение реализует интерфейсы, содержащиеся в API, и предоставляет их плагинам, загруженным с ServiceLoader.Чистый раствор для «литья» ObservableList <Foo> в список <IFoo>?

Рассмотрим этот код:

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 из Layer s доступен
  • API предоставляет только List из ILayer s

К сожалению, отливка ObservableList<Layer> на List<ILayer> является незаконным.

Возврат List<Layer> в API предоставит internalMethod() из Layer, так что это не годится.

Я мог бы также иметь private ObservableList<ILayer> layers, то на самом деле хранить Layer там внутри, но это потребует литья его пункты каждый раз, когда я использую internalMethod(), и я не в восторге от этой идеи.

Есть ли чистое решение для этой проблемы?

+0

вы могли бы получить за ошибки отлитых с 'возвратом (List ) (Список ) слои;' – wero

ответ

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); не компилируется. Это может быть или не быть проблемой.

+0

Это хорошая идея, но она полностью не решает мою проблему. Мне все равно придется использовать при вызове 'getLayers()' в других основных классах приложений. – gronostaj

+1

Вы можете добавить пакет private 'ObservableList getLayersNoCast()' или что-то в этом роде ... – assylias

+0

@gronostaj: Но тогда вы снова просачиваете 'Layer' в другие базовые классы, не так ли? – user140547

-1

Что относительно private ObservableList<? extends ILayer> layers = FXCollections.observableArrayList();?

+1

OP все равно придется использовать для вызова метода internalMethod. – Puce

1

Другой soltuion может быть использование метода 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. Из Javadoc:

После того, как список связан с ObservableList, список не должен быть изменен непосредственно больше. Это приведет к неожиданным результатам.

2

@ ответ assylias представляет собой самое чистое решение, доступное на вопрос. Дело в том, что List<Layer> без вопросов не является a List<ILayer> (учтите: вы можете добавить ILayer, который не является Layer только последним), и Java не признает, что отливки, которые он распознает, никогда не могут быть правильными.

Если вы определенно хотите, чтобы вернуться точно List<ILayer>, однако, то у вас есть по крайней мере три хорошие альтернативы:

  1. Создать в первую очередь на List<ILayer>, и жить с реквизитом литья внутри Plotter.getLayers(). Поскольку вы (очень разумно) предпочитают не отбрасывать, вы можете также
  2. Использование List<Layer> внутри, а затем создать новыйList<Ilayer> вернуться из метода, например

    return new ArrayList<ILayer>(layers); 
    
  3. В качестве варианта на (2), вы можете создать список обертки вместо копии, полагая, что это нормально для возвращаемого список будет нередактируемым:

    return Collections.<ILayer>unmodifiableList(layers); 
    

    Обратите внимание на использовании явного вида р arameter, чтобы дать вам желаемую параметризацию типа результата. Обратите также внимание, однако, что эта альтернатива устанавливает еще больше ограничений на использование возвращаемого значения, чем подход List<? extends ILayer> ассетин.

+0

О, теперь кажется очевидным, почему такое кастинг является незаконным. Спасибо за разъяснения! – gronostaj

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()); 
+0

Весь смысл 'innerMethod()' состоит в том, чтобы иметь * внутренний * метод, который недоступен через API. Мне нужно «ObservableList» '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''' '' '' '' '' '' '' – gronostaj

+0

Ну, я думаю, вы можете использовать 'List ', как это было предложено другими, или поддерживает два списка, если функция ObservableList обеспечивает функциональность, чтобы синхронизировать их, поскольку List не может одновременно иметь типы' ILayer' и 'Layer'. – user140547

Смежные вопросы