Reputation: 67252
Imagine the following code:
class A {}
class B extends A {}
interface IA extends Iterable<A> {}
interface IB extends Iterable<B> {}
Ideally, I would like the interface IB
to be able to also extend IA
because it does in fact allow you to retrieve A
s.
interface IB extends Iterable<B>, IA {}
or even
interface IB extends Iterable<B> implements IA {}
However, the compiler really dislikes both of those and it would make my code much better if this was allowed as conceptually B can be used as A without up-casting everywhere
What solutions are available for me to solve this problem?
Upvotes: 5
Views: 659
Reputation: 45433
A type cannot have two super types G<X> and G<Y> where X!=Y
- probably due to erasure.
One way to solve your problem is to use a type parameter for the type to be iterated
interface IA<X extends A> extends Iterable<X>
{
@Override Iterator<X> iterator();
}
interface IB extends IA<B>
{
@Override Iterator<B> iterator();
}
I'd usually avoid that, it's just more complexity.
In your example, extending Iterable<A>
is probably not a good idea in the first place. For example
class Team implements Iterable<Member>
{
public Iterator<Member> iterator() { ... }
}
for(Member member : team) ...
but a team concept probably is broader than just a collection of members. It might be clearer to
class Team
{
Iterable<Member> members() { ... }
}
for(Member member : team.members()) ...
In that case, your types can be designed as
interface IA
{
Iterable<? extends A> members();
}
interface IB extends IA
{
@Override
Iterable<? extends B> members();
}
One can also question whether Iterable
should have been more relaxing, instead of
public interface Iterable<T>
Iterator<T> iterator();
would it be better to
public interface Iterable<T>
Iterator<? extends T> iterator();
Upvotes: 2
Reputation: 272467
The non-covariance of generics means that what you want isn't viable (at least not in the general case).
However, perhaps wildcards would solve your specific problem? e.g.
void methodThatOperatesOnA(Iterable<? extends A> it) {
...
}
This will allow you to extract elements from it
as if they were A
s, but the compiler will prevent you from inserting objects,* because it can't guarantee that invariants would be maintained.
null
Upvotes: 9
Reputation: 12766
Generics in Java are not covariant -- that is, List<String> instanceof List<Object>
is false. Thus, IB
is not an instance of IA
and hence is the cause of your compiler woes, I believe.
Imagine a new class, class C extends A {}
. C IS-A A, and thus C would be a legitimate value for IA to contain -- but not for IB. Thus, if you tried to use IB as an instance of IA, you could insert a C and break the type-safety guarantees.
Upvotes: 1