java: converting from List<SomeClass> to List<SomeInterface> when SomeClass implements SomeInterface
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开发者_JAVA技巧) { 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;
}
}
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.
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;
}
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.
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));
return list;
}
精彩评论