2

Attempting to generics-ify some legacy code, I'm stuck. I have a ParentObject which wraps a ChildObject to provide group-type operations on the ChildObject. Most usefully, it allows iteration over the collection of child objects. When I try to add some generics to the mix, I can't work out how to make it play friendly with the iterator method without either a "naming clash" error, or in the example below, a "The return type is incompatible with Iterable.iterator()" error. Any suggestions? (Bonus question - is there a better way to write the avoid thegetChildObjectByIndex() method to avoid type erasure complier warning other than suppressing the warnings?) Thanks a lot in advance for any help

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();  
    }  

}
3
  • 2
    Are those ? super and T extends absolutely necessary? If not, just remove them. Else update your question to clarify the functional design requirement about those necessities more. Only then we can suggest the best suitable generifications. Commented Jul 22, 2010 at 15:45
  • From your description, I'm willing to bet super doesn't mean what you think it means. Commented Jul 22, 2010 at 15:56
  • You would have won that bet, but not any more... Commented Jul 22, 2010 at 16:49

2 Answers 2

3

If ParentObject only contains one subtype of ChildObject, you could parametrize ParentObject on that type:

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();
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

That's the answer! For my first dabbling with generic classes, my mistake was not getting my head around the fact that you define the generic type at the class level just the once. I had started on the List and worked upwards, getting totally confused.
0

Seems like in your example ParentObject is some sort of container class that really has no relationship to ChildObject. Does ChildObject extend ParentObject? If so, that seems less than ideal. Also seems like you should be using "extends" in the generics instead of super, unless I'm just misunderstanding what you're trying to do. What about the following? Or is that too simple for what you want to do?

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

Also I might cache a copy of the unmodifiable collection and use it each time you need an iterator instead of constructing one on the fly.

3 Comments

Regarding caching the unmodifiable collection - all that Collections.unmodifiableCollection() does is give you a pointer to the original object in memory, but one which throws an unsupported exception when you call one of the mutating collection methods. The collection object isn't cloned or copied, so it's an inherently cheap operation. As my List is mutable, I think the overhead of managing the cached copy to keep it in synch would be more expensive than getting a new unmodifiable collection each time. Any thoughts?
It is my understanding that the Collections.* methods return new collections that wrap the original collection, throwing exceptions all mutating operations and delegating the rest to the original collection. If that's the case, then you're essentially creating a new wrapper class each time someone calls your iterator() method. If you're going to be mutating the collection, my suggestion would be to store two copies: the original un-wrapped version which you'd use when you need to change it, and the wrapped immutable version you'd use whenever you need to pass back an iterator.
Side note: it might be easier to just have your container class extend one of the collections classes, like ArrayList, then override the methods you feel like changing (e.g. iterator). Your class could store an immutable "version" of itself internally for use whenever it needs to pass back an immutable iterator. Since the immutable wrapper is backed by the original collection, it will reflect any changes you make to the original.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.