Reputation: 3047
I have two parent classes and two subclasses of them. I have problem with return type of iterator method which always is parent class type and subclass can't override it.
Please consider codes below:
public class Attribute {}
public class Attributes implements Iterable<Attribute> {
protected ArrayList<Attribute> attrs = new ArrayList<Attribute>();
public Iterator<Attribute> iterator() {
return this.attrs.iterator();
}
}
// -------
public class Attr extends Attribute {}
public class Attrs extends Attributes {}
// -------
for (Attr attr : attrs) {
// Error! attrs.iterator() returns Attribute not Attr
}
for (Attribute attribute : attrs) {
Attr attr = (Attr) attribute;
// Works
}
Is it possible to override iterator return type?
Upvotes: 2
Views: 1290
Reputation: 17226
So the following override is acceptable:
public class Baa{
.....
public Baa returnSomething{
return this;
}
}
public class Foo extends Baa{
.....
@Override
public Foo returnSomething{
return this;
}
}
Even if you're looking at the object as a baa everthing still works
public static void main(String[] args){
Baa test=new Foo(); //a Foo is a Baa so no problem
Baa getSomething=test.returnSomething(); //returns a Foo, but a foo can be used as as a baa so no problem
}
It would seem convinient if the same occured for the collections however you can trivially create an example that would have runtime errors
Collection<Intger> numbers=new ArrayList<Intger>();
Collection<Number> falseNumbers=(Collection<Number>)numbers; //pretend not an error
falseNumbers.add(new Double()); ////eeeek!
Using generics, as described within Returning a Collection from a method that specifies that it returns Collection, we can indicate the minimum and maximum bounds of a collection (basically say that they can only contain objects which can be cast to a certain type. By using this we can create an iterator that does not require casting:
public class Attribute {
protected ArrayList<Attribute> attributes = new ArrayList<Attribute>();
public Attribute(){
//holding an array of yourself seems strange, but I am replicating the code in the question
attributes.add(this);
}
public Iterator<? extends Attribute> iterator() {
return this.attributes.iterator();
}
public static void main(String[] args){
Attribute attribute=new Attribute();
Iterator<? extends Attribute> it1=attribute.iterator();
Attribute attributeInternat=it1.next();
Attr attr=new Attr();
Iterator<? extends Attr> it2=attr.iterator();
Attr attrInternat=it2.next();
}
}
public class Attr extends Attribute{
protected ArrayList<Attr> attrs2 = new ArrayList<Attr>();
public Attr(){
//holding an array of yourself seems strange, but I am replicating the code in the question
attrs2.add(this);
}
@Override
public Iterator<? extends Attr> iterator() {
return this.attrs2.iterator();
}
}
This ensures that there is no casting and everything is completely type safe.
Upvotes: 1