Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java: converting from List<SomeClass> to List<SomeInterface> when SomeClass implements SomeInterface

Tags:

java

generics

I'm having a brain cramp: I have a public interface SomeInterface and a static private class SomeClass and am trying to return a List<SomeInterface> from one of my methods, but I get the error (on the return list; line below):

Type mismatch: cannot convert from List<GenericList1.SomeClass> to
List<GenericList1.SomeInterface>

How can I fix this without having to create a new list?

Clarification: I do not want the list to be created as List<SomeInterface> (perhaps the obvious solution) because privately I want to maintain a List<SomeClass> to allow future access to SomeClass's methods beyond the ones in the public interface. This isn't shown in the example case below, but in my real program I need this.

import java.util.ArrayList;
import java.util.List;

public class GenericList1 {

    public interface SomeInterface
    {
        public int getX();
    }

    private static class SomeClass implements SomeInterface
    {
        final private int x;
        @Override public int getX() {
            return this.x;
        }       
        public SomeClass(int x) { this.x = x; }
    }

    public static void main(String[] args) {
        List<SomeInterface> list = createList(10);
        printList(list);
    }

    private static void printList(List<SomeInterface> list) {
        for (SomeInterface si : list)
            System.out.println(si.getX());
    }

    private static List<SomeInterface> createList(int n) {
        List<SomeClass> list = new ArrayList<SomeClass>();
        for (int i = 0; i < n; ++i)
            list.add(new SomeClass(i));
        return list;
    }
}
like image 684
Jason S Avatar asked Feb 10 '11 15:02

Jason S


3 Answers

You should redefine your method as

private static List<? extends SomeInterface> createList(int n) { ...

and similarly, the other list declarations. This allows you to deal with generic lists polymorphically, as long as you only read values from them.

(Should you want to add new elements to a list, you should use List<? super SomeInterface> instead.)

This idiom is known by the abbreviation PECS - Producer: Extends / Consumer: Super, coined by Josh Bloch in Effective Java 2nd Edition, Item 28.

As others have noted, this idiom is needed because a List<SomeClass> is not a List<SomeInterface>, even when SomeClass implements SomeInterface. The reason for this is thoroughly explained in the referred document.

like image 197
Péter Török Avatar answered Nov 05 '22 02:11

Péter Török


Contrary to popular belief, List<Derived> has absolutely no relation to List<Base>, even if Derived extends Base.

Just because the classes share a hierarchy doesn't mean that collections of them do. This is true for generics in general.

You could alter your code as follows:

private static List<SomeInterface> createList(int n) {
        List<SomeInterface> list = new ArrayList<SomeInterface>();
        for (int i = 0; i < n; ++i)
            list.add(new SomeClass(i)); //add a SomeClass object - valid, because it has the interface
    return list;
}
like image 39
pnt Avatar answered Nov 05 '22 02:11

pnt


I would change the signatures of the methods like so:

private static void printList(List<? extends SomeInterface> list) {
    for (SomeInterface si : list)
        System.out.println(si.getX());
}

private static <T super SomeClass> List<T> createList(int n) {
    List<T> list = new ArrayList<T>();
    for (int i = 0; i < n; ++i)
        list.add(new SomeClass(i));
    return list;
}

Using a wildcard in a return type is often a bad idea, because it forces you to use wildcards in all calling methods. I made createList a generic method so that the return type can be as flexable as possible while still being as specific as possible.

like image 43
ILMTitan Avatar answered Nov 05 '22 01:11

ILMTitan