Recently, I've been doing work with interfaces and extending interfaces, as in:
public interface Superclass {
// ...
}
public interface Subclass extends Superclass {
// ...
}
I have classes which implement a third interface:
public interface DoesStuff {
Iterator<Superclass > iterator();
// ...
}
You would think that an implementation of DoesStuff could return an Iterator
public class MyClass implements DoesStuff {
// ...
private Set<Subclass > myThings = new HashSet<Subclass >();
// ...
public Iterator<Superclass > iterator() {
return myThings.iterator();
}
// ...
}
Unfortunately, the Wall of Erasure means we can't do this. Try it for yourself if you don't believe me.
So, what to do? Happily, there is a simple answer. We create an adapter that adapts the subclass iterator to the superclass iterator:
/**
* Adapts a derived class iterator to a base class.
*/
public class IteratorAdapter<B, D extends B > implements Iterator<B > {
private Iterator<D> d;
public IteratorAdapter(Iterator<D > aD) {
d = aD;
}
public boolean hasNext() {
return d.hasNext();
}
public B next() {
return d.next();
}
public void remove() {
d.remove();
}
}
The sad thing is that this shouldn't be necessary. It's trivial. It shouldn't have to be written by developers. The simplicity of this is a clear sign that we need to tear down the Wall of Erasure.
Will it happen any time soon? I hope so. Though I'm hearing talk that "erasing erasure" is probably off the table for Java 7. That would be a truly sad thing.
5 comments:
Actually this is the wall of not understanding generics properly, not erasure.
Let's change DoesStuff to:
public interface DoesStuff {
....Iterator<? extends Superclass> iterator();
}
And live happily ever after.
Well, I live happily ever after...or until I want to make DoesStuff Iterable. If I do that,I get a type clash:
public interface DoesStuff extends Iterable<Superclass> {
Iterator<? extends Superclass> iterator();
}
I haven't found a way to genericize the extends of Iterable to make it work. Perhaps you have another solution, Ricky?
You can simply use a generic type parameter ... for instance ...
public interface DoesStuff<T extends Superclass> extends Iterable<T> {
Iterator<T> iterator();
}
The suggestion by anonymous doesn't seem to help. If I do as suggested I still can't make my concrete class Iterable.
I'd love to solve this problem without needing the iterator adaptor (and without obfuscating the code with too much generics gobblygook).
Perhaps someone could provide a complete example that works? If they send it to me via email, I'll format it in a blog posting and give them credit.
Thanks Brian. This old (by Internet time standards) post of yours came in real handy when I wanted to return a String iterator for a SortedMap's keys.
Post a Comment