Read - What is covariance?
Covariant Return type
Now we have a look at covariant method parameters, which are considered unsound.
Consider the following code:
Now there is absolutely no reason why this should not work. It is trivially inferable that the above code treats ilist as covariant in the list-type - and that therefore this code is statically correct.
Of course Java's typing has never been particularly smart. List.add(T1) is contra-variant in t1, and T2 List.get(int) is co-variant in t2; so the Java compiler is correct to infer that in the general case List and List are substitutable iff t1 == t2.
If we can't declare a generic parameter to be covariant in its type parameter we have a serious problem - it means that any non-trivial algorithm involving collections is going to run afoul of this. You might consider trying to cast your way around it:
but not surprisingly that didn't work.
If you continue to hack at it you might try a double cast via a non-generic List.
This works but leaves us with the unchecked/unsafe operation warning:
Now this is a perfectly reasonable warning - it is unchecked; it is unsafe; and more importantly it does violate encapsulation. The problem here is that the caller should not be defining the type invariants of the callee - that's the job of the method signature!
The correct solution is to allow us to declare covariant() to be covariant in its argument; and fortunately Java does support this.
To declare an argument to be covariant in its type parameter you can use the extends keyword:
To declare an argument to be contravariant in its type parameter you use the super keyword:
Covariant Return type
Now we have a look at covariant method parameters, which are considered unsound.
Consider the following code:
public interface TestInterface { } public class TestClass implements TestInterface { } import java.util.ArrayList; import java.util.List; public class Test { private List<testclass> list; public TestInterface test() { list = new ArrayList<testclass>(); list.add(new TestClass()); return covariant(list); } public TestInterface covariant(List<testinterface> ilist) { return ilist.remove(0); } }
Now there is absolutely no reason why this should not work. It is trivially inferable that the above code treats ilist as covariant in the list-type - and that therefore this code is statically correct.
Of course Java's typing has never been particularly smart. List
If we can't declare a generic parameter to be covariant in its type parameter we have a serious problem - it means that any non-trivial algorithm involving collections is going to run afoul of this. You might consider trying to cast your way around it:
public TestInterface test() { list = new ArrayList<testclass>(); list.add(new TestClass()); return covariant((List<testinterface>)list); }
but not surprisingly that didn't work.
Test.java:11: inconvertible types found : java.util.List<testclass> required: java.util.List<testinterface> return convariant((List<testinterface>)list); ^ 1 error
If you continue to hack at it you might try a double cast via a non-generic List.
public TestInterface test() { list = new ArrayList<testclass>(); list.add(new TestClass()); return covariant((List<testinterface>)((List)list)); }
This works but leaves us with the unchecked/unsafe operation warning:
Note: Test.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
Now this is a perfectly reasonable warning - it is unchecked; it is unsafe; and more importantly it does violate encapsulation. The problem here is that the caller should not be defining the type invariants of the callee - that's the job of the method signature!
The correct solution is to allow us to declare covariant() to be covariant in its argument; and fortunately Java does support this.
To declare an argument to be covariant in its type parameter you can use the extends keyword:
public TestInterface covariant(List<? extends TestInterface> ilist) { return ilist.remove(0); }
To declare an argument to be contravariant in its type parameter you use the super keyword:
public void contravariant(List<? super TestClass> clist, TestClass c) { clist.add(c); }
No comments:
Post a Comment