AHHP
AHHP

Reputation: 3047

Override iterator type

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

Answers (1)

Richard Tingle
Richard Tingle

Reputation: 17226

An overridden method must be compatible with the new method.

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

}

This doesn't work for generics

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!

Contravariance saves the day

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

Related Questions