Returning a Collection<ChildType> from a method that specifies that it returns Collection<ParentType>
Asked Answered
S

1

0

I'm trying to write a method that will return a collection (eg Arraylist) that is declared using generics as containing either a parent class or a class that extends the parent class. For this method the objects in the collection will always be used as instances of the parent class but in other contexts they are used differently hence them being held separately. For example:

import java.util.ArrayList;
import java.util.Collection;

public class TestClass {

    public static int PARENTTYPE=0;
    public static int CHILDTYPE=1;

    Collection<ParentClass> parentHolder=new ArrayList<ParentClass>();
    Collection<ChildClass> childHolder=new ArrayList<ChildClass>();

    public TestClass(){

    }

    public Collection<ParentClass> getHolder(int type){
        if (type==PARENTTYPE){
            return parentHolder;
        }else if (type==CHILDTYPE){
            return childHolder; //<--incompatible types
        }else{
            throw new RuntimeException("Not a valid type");
        }
    }

    public static void main(String args[]){
        TestClass test=new TestClass();

        Collection<ParentClass> col1=test.getHolder(PARENTTYPE);
        Collection<ParentClass> col2=test.getHolder(CHILDTYPE);
    }
}


public class ParentClass {
}


public class ChildClass extends ParentClass{
}

However I receive an incompatible types exception at the indicated line. How can I write the method such that I can return collections declared as containing any objects that extend a certain class.

Sacring answered 29/6, 2013 at 19:35 Comment(0)
S
4

Reason for the error
The reason the exception is raised is because Collection<ParentClass> can have any object that extends ParentClass added to it. For example an instance of the following class could be added to it

public class OtherChildClass extends ParentClass{
}

But because the "real" collection is a Collection<ChildClass> this would raise an error. Or even if you added a ParentClass to it it would raise an error. All in all not good.

Solution
This can be solved however by using Collection<? extends ParentClass> in your type declaration. The meaning of this is that you are returning a Collection that is declared as containing some class that could be cast to ParentClass and is called contravariance. Such a collection cannot have new objects added to it (with the admitted exception of null) because the compiler could never guarantee that what you are trying to put into it is valid. But it can guarantee that whatever comes out of it can be cast to ParentClass and used as such. See here for more details. A usage example is as follows:

import java.util.ArrayList;
import java.util.Collection;

public class TestClass {

    public static int PARENTTYPE=0;
    public static int CHILDTYPE=1;

    ArrayList<ParentClass> parentHolder=new ArrayList<ParentClass>();
    ArrayList<ChildClass> childHolder=new ArrayList<ChildClass>();

    public TestClass(){
        parentHolder.add(new ParentClass());
        childHolder.add(new ChildClass());
    }

    public ArrayList<? extends ParentClass> getHolder(int type){
        if (type==PARENTTYPE){
            return parentHolder;
        }else if (type==CHILDTYPE){
            return childHolder; //<-- no longer incompatible types
        }else{
            throw new RuntimeException("Not a valid type");
        }
    }

    public static void main(String args[]){
        TestClass test=new TestClass();

        ArrayList<? extends ParentClass> col1=test.getHolder(PARENTTYPE);
        ArrayList<? extends ParentClass> col2=test.getHolder(CHILDTYPE);

        ParentClass childCastToParent=col2.get(0);

    }
}

N.B. arraylists have been used for brevity but the same applied to all collections

Sacring answered 29/6, 2013 at 19:35 Comment(2)
Just a note regarding the "read only" statement: You can still add null to such a collection, and you can remove elements from it, so it is not completely read only.Lightship
@PhilippWendler Thanks, thats something I hadn't considered (fixed)Sacring

© 2022 - 2024 — McMap. All rights reserved.