LiraNuna
LiraNuna

Reputation: 67252

Java: Interface generic of a superclass

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 As.

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

Answers (3)

irreputable
irreputable

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

Oliver Charlesworth
Oliver Charlesworth

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 As, but the compiler will prevent you from inserting objects,* because it can't guarantee that invariants would be maintained.


* other than null and a few other wacky cases

Upvotes: 9

Thorn G
Thorn G

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

Related Questions