views:

551

answers:

5

I have an interface for a variety of classes, all of which should implement Iterator, so I have something like

public interface A extends Iterable<A> { ...otherMethods()... }

For the concrete classes, however, this means I must use

public class B implements A { public Iterator<A> iterator() {...} }

when I'd prefer (or at least, think I'd prefer) to use public Iterator<B> iterator() {...} so that concrete use of the class could have the explicit type (in case I wanted methods that weren't in the interface to be available, or some such. Maybe that should never come up? Or it's poor design if it does?

The flipside is that using the Iterator interface

public interface A extends Iterator<A> { ...otherMethods()... }

the concrete classes compile just fine with

public class B implements A { public B next() {...} }

What gives?

+2  A: 

Carl was right, my first answer didn't compile but this seems to. Not sure if it's what you want exactly.

public interface A<T extends A> extends Iterable<T> {

}

public class B implements A<B> {

    @Override
    public Iterator<B> iterator() {
        return null;
    }
Matt
I don't think super-types can use wildcards; at least, that's what my compiler is telling me.
Carl
I had tried this before, but it has the disadvantage during abstract implementation of needingAbstractInterface<ConcreteClass> xwhich seems to defeat the purpose of abstraction, or you're eating warnings about a raw type, which seems to defeat the purpose of generics in the first place (stronger type control).
Carl
A: 

I guess this is what you want:

public interface A<T extends A<?>>  extends Iterable<T>

public class B implements A<B> {
  public Iterator<B> iterator() {...}
}
Kutzi
Regarding your comment that you want a solution which doesn't require the subclass to be parametrized:I don't think that that is possible (however I'm no generics expert).It works if you use the Iterator interface, because in that case you can take advantage of covariance, while generified interfaces are not covariant. See the excellent Generics FAQ by Angelika Langer:http://www.angelikalanger.com/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm
Kutzi
A: 

The concrete class compiles fine because B extends A, and since Java 5 a return type on a subclass can be a subclass of the return type on the superclass.

As for the generic, I have hit the same wall, and you have two options that I know of.

One is to paramaterize A:

 public interface A<T extends A> extends Iterable<T>

then:

public class B implements A<B>

However, that has a disadvantage that you will need to give a parameter of A, instead of having it fixed, even if you want A:

private class NoOneSees implements A<A>

As to the question of if you actually want Iterator<B>, in the case of an Iterable, most likely that is preferable, and considering that these are interfaces, the need to redeclare the parameter is probably reasonable. It gets a little more complicated if you start inheriting concrete classes, and for things that have meaning other than an Iterable, where you may want covariance. For example:

public interface Blah<T> {
    void blah(T param);
}

public class Super implements Blah<Super> {
    public void blah(Super param) {}
}

public class Sub extends Super {
    public void blah(Super param) {}
   //Here you have to go with Super because Super is not paramaterized
   //to allow a Sub here and still be overriding the method.
}

Similarly for covariance, you can't declare a variable of type Iterator<A> and then assign an Iterator<B> in it, even if B extends A.

The other option is live with the fact that further implementation/subclasses will still reference A.

Yishai
The super/sub rabbithole can be fixed a bit by changing the interface declaration from void blah(T param) to <U extends T> void blah(U param);
Carl
+1  A: 

Your design decisions are your own, but I can't think of any reason for EVERY class in a design to implement Iterable. There must be some kind of thing that is contained in a collection but isn't actually a collection itself. I would take a long hard look at the fundamental design. Maybe some iterables will want to return iterators of things that are not related to themselves.

DJClayworth
A: 

The other answers have the right gist - but you'll get the right mechanics by declaring the generic type as

A<T extends A<T>>

This forces a class to return an iterator that is of its own type or lower - so you would be able to declare

class B implements A<B>

but not

class B implements A<A>

which I suspect is closer to what you want (i.e. implementations must return iterators over themselves, rather than simply over As).

Note that your experiences with Iterator vs Iterable stem from the lack of covariance of generic types; an instance of B is an A, but an Iterator<B> is not an Iterator<A>.

Andrzej Doyle