2010-07-22 2 views
2

Пытаемся использовать generics-ify некоторый унаследованный код, я застрял. У меня есть ParentObject, который обертывает ChildObject для предоставления операций типа группы в ChildObject. Наиболее полезно, он позволяет итерации по набору дочерних объектов. Когда я пытаюсь добавить некоторые общие элементы в микс, я не могу понять, как заставить его играть дружелюбно с методом итератора без ошибки «именования» или в приведенном ниже примере «Тип возврата несовместим с ошибкой Iterable.iterator(). Любые предложения? (Bonus вопрос - есть лучший способ, чтобы написать метод избежать thegetChildObjectByIndex(), чтобы избежать типа стирания компиляторов предупреждения, кроме подавления предупреждений?) Спасибо большого заранее за любую помощьПроблема с генераторами Java

public class ParentObject implements Iterable<ChildObject> { 
    protected List<? super ChildObject> theChildObjects; 

    @SuppressWarnings("unchecked") 
    public <T extends ChildObject> T getChildObjectByIndex(int idx) { 
     return (T)theChildObjects.get(idx); 
    } 

    public Iterator<? super ChildObject> iterator() { 
     return java.util.Collections.unmodifiableCollection(this.theChildObjects).iterator(); 
    } 

} 
+2

Это те '? супер' и 'T extends' абсолютно необходимы? Если нет, просто удалите их. Else обновите свой вопрос, чтобы уточнить требования к функциональному дизайну в отношении этих потребностей. Только тогда мы можем предложить наилучшие подходящие генераторы. – BalusC

+0

Из вашего описания, я готов поспорить, что 'супер' не означает, что вы думаете, что это значит. –

+0

Вы бы выиграли эту ставку, но не более ... – Chris

ответ

3

Если ParentObject содержит только один подтип ChildObject, вы можете параметризуете ParentObject на этом типе:

public class ParentObject<T extends ChildObject> implements Iterable<T> { 
    protected List<T> theChildObjects; 

    public T getChildObjectByIndex(int idx) { 
     return theChildObjects.get(idx); 
    } 

    public Iterator<T> iterator() { 
     return java.util.Collections.unmodifiableCollection(this.theChildObjects).iterator(); 
    } 
} 
+0

Вот и ответ! Для моей первой беседы с родовыми классами моя ошибка не была связана с тем, что вы определяете общий тип на уровне класса только один раз. Я начал в Списке и работал вверх, полностью запутавшись. – Chris

0

Похоже, в вашем примере ParentObject является своим родом контейнер класса, который на самом деле не имеет никакого отношения к ChildObject. Поддерживает ли ChildObject ParentObject? Если это так, это кажется менее идеальным. Также кажется, что вы должны использовать «extends» в генериках вместо супер, если только я просто не понимаю, что вы пытаетесь сделать. Как насчет следующего? Или это слишком просто для того, что вы хотите сделать?

public class WidgetContainer<T> implements Iterable<T> { 
    protected List<T> theWidgets; 

    public T getWidgetByIndex(int idx) { 
     return theWidgets.get(idx); 
    } 

    public Iterator<T> iterator() { 
     return Collections.unmodifiableCollection(theWidgets).iterator(); 
    } 
} 

WidgetContainer<SomeWidget> myWidgetContainer = new WidgetContainer<SomeWidget>(); 

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

+0

Что касается кэширования немодифицируемой коллекции - все, что делает Collections.unmodifiableCollection(), дает вам указатель на исходный объект в памяти, но тот, который выдает неподдерживаемое исключение, когда вы вызываете один из методов мутирования коллекции. Объект коллекции не клонирован или не скопирован, поэтому это дешевая операция. Поскольку мой список изменчив, я думаю, что накладные расходы на управление кэшированной копией, чтобы синхронизировать ее, были бы дороже, чем получение новой неизменяемой коллекции каждый раз. Любые мысли? – Chris

+0

Насколько я понимаю, методы Collections. * Возвращают новые коллекции, которые * обертывают * исходную коллекцию, исключая все мутирующие операции и делегируя остальные исходной коллекции. Если это так, то вы по существу создаете новый класс оболочки каждый раз, когда кто-то вызывает ваш метод iterator(). Если вы собираетесь мутировать коллекцию, мое предложение состояло бы в том, чтобы хранить две копии: оригинальную версию без оболочки, которую вы будете использовать, когда вам нужно ее изменить, и завернутую неизменяемую версию, которую вы будете использовать всякий раз вам нужно передать итератор обратно. – jph

+0

Боковое примечание. Возможно, было бы проще, если бы ваш контейнерный класс расширил один из классов коллекций, например ArrayList, затем переопределите методы, которые вы хотите изменить (например, итератор). Ваш класс может хранить неизменяемую «версию» сам по себе для использования, когда ему нужно передать неизменный итератор. Поскольку неизменяемая обертка поддерживается оригинальной коллекцией, она будет отражать любые изменения, внесенные вами в оригинал. – jph