> Allow Different Types of Nested Lists? " />
Multiple Wildcards on Generic Methods Can Confuse the Java Compiler
The Problem:
Using multiple wildcards on generic methods can lead to confusion for both the Java compiler and the programmer. Consider the following example:
public class TwoListsOfUnknowns { static void doNothing(List<?> list1, List<?> list2) {} public static void main(String[] args) { List<String> list1 = null; List<Integer> list2 = null; doNothing(list1, list2); // compiles fine! } }
Two unrelated wildcards can be used to call doNothing with a List
public class TwoListsOfUnknowns2 { static void doSomethingIllegal(List<?> list1, List<?> list2) { list1.addAll(list2); // DOES NOT COMPILE!!! } }
This behavior is expected because list1 and list2 refer to entirely different types.
Confusion arises with the following code:
public class LOLUnknowns1 { static void probablyIllegal(List<List<?>> lol, List<?> list) { lol.add(list); // this compiles!! how come??? } }
This code compiles, but should it? It is possible to have a List> lol and a List
public class LOLUnknowns2 { static void rightfullyIllegal( List<List<? extends Number>> lol, List<?> list) { lol.add(list); // DOES NOT COMPILE! As expected!!! } }
The compiler seems to be doing its job, but confusion persists with the following variation:
public class LOLUnknowns3 { static void probablyIllegalAgain( List<List<? extends Number>> lol, List<? extends Number> list) { lol.add(list); // compiles fine!!! how come??? } }
Shouldn't this code not compile because it may involve a List> lol and a List
To clarify these behaviors, let's simplify the code back to LOLUnknowns1 and try to invoke probablyIllegal:
public class LOLUnknowns1a { static void probablyIllegal(List<List<?>> lol, List<?> list) { lol.add(list); // this compiles!! how come??? } public static void main(String[] args) { List<List<String>> lol = null; List<String> list = null; probablyIllegal(lol, list); // DOES NOT COMPILE!! } }
Even with the same type for both wildcards, the code does not compile. However, a null type for the first argument compiles:
public class LOLUnknowns1b { static void probablyIllegal(List<List<?>> lol, List<?> list) { lol.add(list); // this compiles!! how come??? } public static void main(String[] args) { List<String> list = null; probablyIllegal(null, list); // compiles fine! } }
The Answer:
The confusion stems from a misunderstanding of what a nested wildcard, such as List>, actually means. Java Generics is invariant, meaning relationships among types do not hold for generic instantiations. This applies to nested lists as well:
A List<String> is (captureable by) a List<?>. A List<List<String>> is NOT (captureable by) a List<List<?>>. A List<List<String>> IS (captureable by) a List<? extends List<?>>.
Therefore, a List> represents a list whose elements are lists of any type, not specifically the same type. This means that the following code is legal:
List<List<?>> lolAny = new ArrayList<>(); lolAny.add(new ArrayList<Integer>()); lolAny.add(new ArrayList<String>());
In Summary
The Java compiler's behavior with multiple generic wildcards is determined by capture conversion rules. Understanding the limitations of wildcard capture is crucial to avoid confusion.
The above is the detailed content of Why Does a List> Allow Different Types of Nested Lists?. For more information, please follow other related articles on the PHP Chinese website!