I tried to cast an Iterator of a class to an iterator of a subclass of said class. This gave me an "inconvertible types" error. Why is this not possible and what is the most elegant way to work around it? (Or alternatively, why is it a bad idea if it is?)
Using a for-each loop is not a solution in this case: I'm trying to implement iterator()
and the easiest way to do this would be to return the iterator()
of one of my class' fields, but that one doesn't have the exact required type. I can't change the signature of my iterator()
either.
public interface SomeoneElsesInterface {
public Iterator<SomeoneElsesInterface> iterator();
}
public abstract class MyAbstractClass implements SomeoneElsesInterface {
final MyAbstractClass[] things;
public MyAbstractClass(SomeoneElsesInterface... things) {
this.things = (MyAbstractClass[]) things;
}
}
public class MyClass extends MyAbstractClass {
public MyClass(MyAbstractClass thing1, MyAbstractClass thing2) {
super(thing1, thing2);
}
public Iterator<SomeoneElsesInterface>() {
return (Iterator<SomeoneElsesInterface>) Arrays.asList(things).iterator();
}
}
I could, of course, just change the type of things
. However, I would need a lot of casts in other places in that case. I do know that my constructor won't be called with objects that are not MyAbstractClass
s but I cannot change the interface anyway.
This looks to be as simple as using explicit type argument specification:
public class MyClass extends MyAbstractClass {
// ...
public Iterator<SomeoneElsesInterface> iterator() {
return Arrays.<SomeoneElsesInterface>asList(things).iterator();
}
}
The problem is that Arrays#asList()
is inferring that you want a list of type List<MyAbstractClass>
, which will yield an iterator of type Iterator<MyAbstractClass>
. Since Iterator
's type parameter is not covariant, you cannot supply an Iterator<MyAbstractClass>
where an Iterator<SomeoneElsesInterface>
is required. By forcing Arrays#asList()
to create a list of type List<SomeoneElsesInterface>
, as shown above, you also wind up with the intended iterator type coming back from your call to Iterable#iterator()
.
The author of SomeoneElsesInterface
would have been kinder to specify the return type of its iterator()
method as Iterator<? extends SomeoneElsesInterface>
.
I think from your question you're trying to do something like this:
Iterator<Object> original = ...
Iterator<String> converted = (Iterator<String>)original;
Is that correct?
If so, that is, unfortunately, impossible. The problem is that original
can contain objects that are not String
s, so allowing that cast would break the generics contract, i.e. converted
could contain something that is not a String
.
I don't think there is an elegant workaround for this.
You say the easiest way to implement iterator()
is to return an instance field's iterator, so I'm guessing you have something like this:
class IterableThing implements Iterable<Foo> {
private Collection<Bar> someStuff;
public Iterator<Foo> iterator() {
return (Iterator<Foo>)someStuff.iterator();
}
}
class Bar {
}
class Foo extends Bar {
}
If someStuff
can be guaranteed to contain only instances of Foo
, then can you declare someStuff
to be a Collection<Foo>
rather than a Collection<Bar>
? If not, then it doesn't really make sense to just return someStuff
's iterator because it might contain something that is not a Foo
.
I guess you need to think about what guarantees you can actually make. If you can't guarantee that someStuff
only contains Foo
s then you will probably have to maintain your own state, or filter the contents of someStuff
on demand.
EDIT: You've updated your question with code. Awesome.
So it looks like you're actually trying to return an iterator over the superclass of the type. That makes things a lot easier.
In your particular case, you can probably solve it with this:
return Arrays.<SomeoneElsesInterface>asList(things).iterator();
It'll generate some warnings, but that's OK because you know that you've guaranteed type safety.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With