2015-10-16 3 views
0

У меня есть (для меня) сложная проблема Java generics. Я прочитал некоторые документы и понял некоторые, но, конечно, не все, что мне нужно. В принципе, для меня попытка решить это приведет к попытке и ошибке.Генераторы Java, связанные с наследованием, и это

В следующем случае я даю сжатый пример моего кода, один раз без каких-либо дженериков (так можно надеяться понять, чего я хочу достичь), а другой с некоторыми дополнениями, которые приближаются к решению. Пожалуйста, исправьте мою вторую версию и/или укажите мне конкретную документацию. (У меня есть общая документация Java-дженериков, но у моего кода, похоже, есть несколько препятствий и трудно найти правильное решение)

О моем примере: Существует абстрактный базовый тип и несколько вариантов реализации (предоставляется только один вариант). Метод combine() вызывает getOp1(), который решает (в зависимости от <some condition>), если он должен работать на своем собственном экземпляре или на новом. После вычисления возвращается целевой экземпляр.

abstract class Base { 
    protected final Base getOp1() { 
     if(Util.isConditionMet()) { return getNewInstance(); } 
     else { return this; } 
    } 
    protected abstract Base getNewInstance(); // returns a new instance of an implementing class 
    public abstract Base combine(Base other); 
} 

class Variant extends Base { 
    public Variant getNewInstance() { return new Variant(); } 
    public combine(Variant op2) { 
     Variant op1 = getOp1(); 
     op1.calculate(op2); 
     return op1; 
    } 
    private void calculate(Variant other) { /* some code */ } 
} 

Версия с некоторыми генериками. Эта версия неисправна и не компилируется.

abstract class Base<T extends Base<T>> { 
    protected final T getOp1() { 
     if(Util.isConditionMet()) { return getNewInstance(); } 
     else { return this; } 
    } 
    protected abstract T getNewInstance(); // returns a new instance of an implementing class 
    public abstract T combine(T other); 
} 

class Variant<T extends Variant<T>> extends Base<T> { 
    protected T getNewInstance() { return new Variant(); } 
    public T combine(T op2) { 
     T op1 = getOp1(); 
     op1.calculate(op2); 
     return op1; 
    } 
    private void calculate(T other) { /* some code */ } 
} 
+1

'... и не compile.' Пожалуйста, включите сообщения об ошибках компилятора с соответствующими номерами строк –

+0

Ну, ваши ошибки компилятора, безусловно, связаны с тем, что вы возвращаете 'this' в метод getOp1' Base. Как вы можете вернуть «это» из абстрактного объекта? Вся суть абстрактного объекта заключается в том, что он не может быть создан. – thatidiotguy

+0

@thatidiotguy, потому что он делегирует getNewInstance(), который также является абстрактным –

ответ

0

Для того, чтобы этот код работает, вам необходимо решить вопросы типа несовместимости: заменить T возвращающихся типов, Base<T> и отбрасывать результат Variant#getOp1() к Variant<T>, чтобы вызвать calculate() на нем (это безопасно здесь потому, что Variant#getOp1() всегда возвращает Variant:

abstract class Base<T extends Base<T>> { 
    protected final Base<T> getOp1() { 
     return condition() ? getNewInstance() : this; 
    } 
    protected abstract Base<T> getNewInstance(); 
    public abstract Base<T> combine(T other); 
} 



class Variant<T extends Variant<T>> extends Base<T> { 
    protected Base<T> getNewInstance() { 
     return new Variant(); 
    } 

    public Base<T> combine(T op2) { 
     Variant<T> op1 = (Variant<T>) getOp1(); // <- explicit cast 
     op1.calculate(op2); 
     return op1; 
    } 

    private void calculate(Base<T> other) { 
     // ... 
    } 
} 

Btw, я до сих пор не вижу причин такой сложной структуры типа.

+0

Ваш ответ - отправная точка для меня. Спасибо –

+0

Как использовать Variant >? если я хочу использовать Variant, я должен использовать дочерний вариант Variant для замены T, но у меня не было Variant. Это то же самое, что и курица и яйцо. Можете ли вы привести мне пример? Спасибо – xxxzhi

+0

@ Саша Салауу. Вероятно, вы имели в виду 'private void calculate (вариант другой) {', не так ли? Просто потому, что ваш фрагмент кода помечен как решение –

0

Я видел пару таких комбинационных, эксплуатационных классов, хотя и не слишком сложный. Возможно, наследование не является правильным инструментом.

Лучше использовать механизм поиска возможностей, функций.

class Base { 

    // Untyped 
    private Map<Class<?>, Object> capabilities = new HashMap<>(); 

    protected <I> void register(Class<I> intf, I obj) { 
     capabilities.put(intf, obj); 
    } 

    public <T> Optional<T> lookup(Class<T> intf) { 
     Object obj = capabilities.get(intf); 
     return obj == null ? Optional.emtpy() : Optional.of(intf.cast(obj)); 
    } 
} 

interface Flying { 
    void fly(double altitude); 
} 

Base pelican = new Pelican(); 
Flying flying = pelical.lookup(Flying.class).orElse(null); 
flying.fly(0.5); 

Это также позволяет динамически изменять и комбинировать вещи в отношении двух аспектов.

+0

Ну, множество вариантов статично во время выполнения, поэтому нет необходимости в динамической регистрации. Время от времени мы добавим новый вариант, а затем перекомпилируем. –

+0

Хорошо, тогда развязка вроде этого не нужна. –

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