Страницы

Поиск по вопросам

суббота, 11 января 2020 г.

Реализация одного и того же интерфейса для двух разных типов в одном классе

#java


Есть некий интерфейс:

public interface ViewableParent extends Viewable{

    interface ListChangeCallback{
        void doWhenChange(C object);
    }

    ListChangeCallback getAddedSubListCallback();
    ListChangeCallback getRemovedSubListCallback();

    default ListChangeListener getChangeListener(){
        return c -> {
            while (c.next()){
                if (c.wasAdded()) {
                    //Проходимся по добавленным элементам
                    c.getAddedSubList().forEach(e -> {
                        //Вешаем на проперти стиля слушатель, который при изменении
стиля потомка меняет стиль у родителя
                        e.styleProperty().addListener((observable, oldValue, newValue) ->
                                refreshStyle(c.getList(), ViewableParent.this,false));
                        //Выполяняем код add коллбэка
                        if (getAddedSubListCallback()!=null)
                            getAddedSubListCallback().doWhenChange(e);
                    });
                    //Пересчитываем стиль для родителя исходя из стилей добавляемых
потомков
                    refreshStyle(c.getAddedSubList(), ViewableParent.this,true);
                } else if (c.wasRemoved() || c.wasUpdated()) {
                    //Иначе, если что-то было удалено или обновлено (здесь не уверен
в корректности события update)
                    //Так же пересчитываем стиль для родителя
                    refreshStyle(c.getList(), ViewableParent.this, false);
                    //Выполняем код remove коллбэка
                    if (getRemovedSubListCallback()!=null) 
                        c.getRemoved().forEach(e -> getRemovedSubListCallback().doWhenChange(e));
                   
                }
            }
        };
    }
}


Параметр типа C обозначает тип потомка для класса, реализующего данный интерфейс.
4 из 5 классов имеют только один тип потомка, однако есть один класс, в котором потомков
2. Если в объявлении класса 2 раза написать один и тот же интерфейс с разными типами
в дженерике, само собой IDE выдает ошибку. Подскажите, каким образом можно обойти данное
ограничение с точки зрения архитектуры?
    


Ответы

Ответ 1



У вас не получится реализовать два раза интерфейс с разными параметризированными типами: interface MyInterface{ T method(); } class MyClass implements MyInterface, MyInterface{ ... } Потому, что происходит стирание типов, и в байткоде остаются только интерфейсы с типом Object. Т.е. для java MyInterface и MyInterface одно и тоже. Если бы даже, не происходило стирания, то возникли бы логические ошибки. Как ранее заметил @zRrr может возникнуть ситуация, что класс реализует два одинаковых метода, с разным возвращаемым типом. public A method(){ ... } public B method(){ ... } При вызове которых, не понятно, что вызывать, и в java такой код не является корректным. Если очень хочется, то можно сделать, но опять же, зависит от сигнатур методов интерфейса и не во всех случаях будет работать. Для интерфейса Comparable это выглядит следующим образом: class A implements Comparable { @Override public int compareTo(A o) { return 0; } } class B implements Comparable { @Override public int compareTo(B o) { return 0; } } //E extends A or B class Gibrid implements Comparable { final A a = new A(); final B b = new B(); @Override public int compareTo(E o) { if (o instanceof A) return a.compareTo((A) o); else if (o instanceof B) return b.compareTo((B) o); throw new IllegalArgumentException("object type is not correct." +"It must be A or B"); } } Но здесь мы уходим от проверки типов во время компиляции во время исполнения программы.

Ответ 2



В итоге пришлось отказаться от использования дженерика в описании интерфейса и немного расширить default метод. Не знаю пока, насколько это оправданно и корректно, однако все работает. Единственный момент: не ясно, что в данной ситуации было бы правильней. Вынести интерфейс коллбэка и default метод как статический в утилитарном классе или оставить так? При этом поведение метода переопределять не планируется. public interface ViewableParent extends Viewable{ interface ListChangeCallback{ void doWhenChange(C object); } default ListChangeListener getViewChangeListener(ListChangeCallback addCallback, ListChangeCallback removeCallback){ return c -> { while (c.next()){ if (c.wasAdded()) { //Проходимся по добавленным элементам c.getAddedSubList().forEach(e -> { //Вешаем на проперти стиля слушатель, который при изменении стиля потомка меняет стиль у родителя e.styleProperty().addListener((observable, oldValue, newValue) -> refreshStyle(c.getList(),false)); //Выполяняем код add коллбэка if (addCallback!=null) addCallback.doWhenChange(e); }); //Пересчитываем стиль для родителя исходя из стилей добавляемых потомков refreshStyle(c.getAddedSubList(),true); } else if (c.wasRemoved() || c.wasUpdated()) { //Иначе, если что-то было удалено или обновлено (здесь не уверен в корректности события update) //Так же пересчитываем стиль для родителя refreshStyle(c.getList(),false); //Выполняем код remove коллбэка if (removeCallback!=null) c.getRemoved().forEach(removeCallback::doWhenChange); } } }; } }

Комментариев нет:

Отправить комментарий