I encountered a problem at work, described in code as follows:
package test;import java.util.LinkedList; import java.util.List;public class ListTest { public void func(List<Base> list) { } public static void main(String args[]) { ListTest lt = new ListTest(); List<Derived> list = new LinkedList<Derived>(); lt.func(list); // 编译报错 } }class Base { }class Derived extends Base { }
You need to write a function func here, which can take the list of Base as a parameter. I thought it was okay to pass a Derived list, because Derived is a derived class of Base, so Derived's list should also be a derived class of Base's list, but the compiler reported an error.
To find out the reason, I checked some information on the Internet: Java's generics are not covariant.
Covariance and contravariance of generics are both terms. The former refers to the ability to use a type that is less derived (less specific) than the originally specified derived type, and the latter refers to the ability to use a derived type that is smaller than the originally specified derived type. A more derived (more specific) type.
For example, generics in C# support covariance:
IEnumerable<Derived> d = new List<Derived>(); IEnumerable<Base> b = d;
But Java’s generics do not support covariance. Code similar to the above cannot be compiled in Java.
But interestingly, arrays in Java support covariance, for example:
Integer[] intArray = new Integer[10]; Number[] numberArray = intArray;
Summary: Java's generics do not support covariance, and it is more from the perspective of type safety. This design is not necessarily necessary. For example, C# does not use this design. It can only be said that the designers of Java made a choice between ease of use and type safety.
Finally, back to the original question, to implement such a method func, you can modify it to:
public void func(List list) { }
or use a parameterized type:
public <T> void func(List<T> list) { }
But this also has problems, it will blur the parameter type of func. A better way is not to change func, but to pass a Base type List when passing parameters, which requires converting elements to Base type when adding them to the List.
PS: By limiting the parameter type:
public void func(List<? extends Base> list) { }