Markus A.
Markus A.

Reputation: 12742

Implementing Iterable by returning an Iterator over a sub-type

In the following code:

public class Bar { ... }

public class Foo extends Bar { ... }

public class BarIterable implements Iterable<Bar> {

    List<Foo> foos = ...

    @Override
    public Iterator<Bar> iterator() {
        return foos.iterator();  // Error: Type mismatch ...
    }
}

I get an error at the indicated position since foos.iterator() returns an Iterable<Foo> rather than an Iterable<Bar>. Unfortunately, a simple cast won't work:

return (Iterator<Bar>) foos.iterator();  // Error: Cannot cast ...

Java also doesn't allow me to change the definition of BarIterable to

public class BarIterable implements Iterable<? extends Bar> { ...   // Error: A supertype may not specify any wildcard

What's a good way to resolve this problem that does not involve implementing Iterable<Foo> instead (the Iterable-implementation comes from another interface that doesn't know about Foo)?

Do I have to write myself a wrapper Iterator-class that "unpacks" each value?

Upvotes: 4

Views: 266

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279920

Since Foo is a subtype of Bar, you could just cast the List.

return ((List<Bar>)(List<? extends Bar>) foos).iterator(); 

and suppress the warnings.

Edit by Markus A.:

Or, you can cast the iterator directly using the intermediate-cast-trick:

return (Iterator<Bar>)(Iterator<? extends Bar>) foos.iterator();

End edit


With Java 8, you can stream over the elements and construct a new List of type Bar.

@Override
public Iterator<Bar> iterator() {
    return foos.stream().<Bar>map((foo) -> foo /* considered a Bar */).collect(Collectors.toList()).iterator();  
}

but you're doing a lot of intermediate operations just to view the List from a super type's point of view.

Upvotes: 6

Related Questions