Read-only view of a Java list with more general type parameter
Suppose I have class Foo extends Superclass. I understand why I can't do this:
List<Foo> fooList = getFooList();
List<Superclass> supList = fooList;
But, it would seem reasonable for me to do that if supList were somehow "read-only". Then, everything would be consistent as everything that would come out of an objList would be a Foo, which is a Superclass.
I could probably write a List implementation that would take an underlying list and a more general type parameter, and would then return everything as the more general type instead of the specific type. It would work like the return of Collections.unmodifiableList()
except that the type would be made more general.
Is there an easier way?
The reason I'm considering doing this is that I 开发者_运维知识库am implementing an interface that requires that I return an (unmodifiable) List<Superclass>, but internally I need to use Foos, so I have a List<Foo>. I can't just cast.
Expanding on Kylar's answer:
If the interface is modified to return a List<? extends Superclass>
, the returned list is still effectively read-only (you can get around it by casting, but the compiler will warn). And you can return a List
of any subclass of Superclass
and still satisfy the interface.
Actual code:
public interface MyInterface {
List<? extends Superclass> getList();
}
public class Superclass {}
public class Subclass {}
public class MyClass implements MyInterface {
public List<? extends Superclass> getList() {
return new ArrayList<SubClass>();
}
}
Now, how did I know it should be ? extends Superclass
? I remembered Josh Bloch's mnemonic:
PECS: Producer Extends, Consumer Super.
So if your returned list is a producer (i.e. you are pulling things out of it but not putting things in), you use ? extends
to generalize it.
If, conversely, it were a consumer (i.e. you are adding things to it but not pulling them out), you could use ? super
to generalize it.
If you are using it as both a producer and a consumer, you unfortunately could not generalize it.
See also:
- Why is SomeClass<? super T> not equivalent to SomeClass<T> in Java generic types?
- Java Generics (Wildcards)
- Java Tutorials: Wildcards
- Java Tutorials: More Fun with Wildcards
List<? extends Superclass>
can be used. Then you can return a list of any items that extend Superclass, like so:
public class MoreGeneric {
public static void main(String[] args) {
List<? extends MyClass> someList;
someList = new ArrayList<MyClass2>();
}
abstract public static class MyClass{
abstract void doSomething();
}
public static class MyClass2 extends MyClass{
public void doSomething(){
}
}
}
I refer you to this Wikipedia article for further reading.
In short: In Java, type parameters are invariant (neither co- nor contra-variant). Your observation about the immutability and such are correct, btw. wiki article link
P.S.: If you are interested in a language with a more flexible type system that runs on the JVM you might want to check out Scala.
You can do that just cast
List<Superclass> supList = (List) fooList;
Just be careful when you do that because if Foo isn't a subclass of Superclass then you'll get a runtime cast exception
精彩评论