Ashok Goli
Ashok Goli

Reputation: 5183

Returning List of Generic Type Objects using Wildcards

Requirement:

Declared a method which returns a Generic List based on wildcards. Got the declaration part right, but method call fails.

class Z{/*TemplateClass*/}
public class A extends Z{}
public class B extends Z{}

Test Class:

public List<? extends Z> getGenericList(boolean test){
    if(test){
        List<A> aList = new ArrayList<A>();
        aList.add(new A());
        return aList;
    }
    else{
        List<B> bList = new ArrayList<B>();
        bList.add(new B());
        return bList;
    }
}

Here's my requirement. I need something like the below code.

List<A> testAList = getGenericList(true);
List<B> testBList = getGenericList(false);

Gives me a compiler error.

Type mismatch: cannot convert from List< capture#1-of ? extends Z > to List< A > Type mismatch: cannot convert from List< capture#2-of ? extends Z > to List< B >

I understand the logic behind this. What if we pass a false and store the returned List of B's as a List of A's which is wrong. But when I use the below code:

List<? extends Z> testList = getGenericList(true);

It compiles fine. But it beats my purpose. I'm not able to access the methods in A or B as the testList.get(index) returns Z type objects where I hope to get either A or B.

If my code is absolutely wrong, then any pointers in the right direction would be appreciated.

Upvotes: 2

Views: 503

Answers (2)

Judge Mental
Judge Mental

Reputation: 5239

Not going to happen. You are asking for a type that simply cannot be expressed in the java type system, something like List<A> + List<B>.

The closest thing to that is a form of continuation passing style; you create another abstract class like HomogeneousListAB with a visitor interface and accept method. Then you can return one of those from getGenericList.

abstract class HomogeneousListAB {
    interface Visitor {
        void ifListA( List< A > la );
        void ifListB( List< B > la );
    }
    abstract public accept( Visitor v );
}

// inside getGenericList true branch
final List< A > laOut = // whatever
return new HomogeneousListAB() {
    public accept( Visitor v ) {
        v.ifListA( laOut );
    }
};

Upvotes: 1

Marco RS
Marco RS

Reputation: 8245

You can cast the result which would make the compiler errors go away but you'd have an unsafe cast which seems like a code smell:

List<A> testAList = (List<A>) getGenericList(true);
List<B> testBList = (List<B>)getGenericList(false);

Also if the boolean input is not a strict requirement you can do something like this as well:

public <T extends Z> List<T> getGenericList(Class<T> klass) {
        List<T> list = new ArrayList<T>();
        try {
            list.add(klass.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return list;
    }

Upvotes: 3

Related Questions