Using Java Generics Wildcard vs Typed Generics

I read the following section in a Java tutorial: http://docs.oracle.com/javase/tutorial/java/generics/capture.html

This starts with the following code causing an error due to the fact that the capture cannot be converted to an object, so the set method cannot confirm that the object has capture type # 1:

 import java.util.List; public class WildcardError { void foo(List<?> i) { i.set(0, i.get(0)); } } 

I somewhat understand the reasons for this. i.get returns an object, and the compiler cannot determine whether the object is capture type # 1, so it cannot match it with the second argument as a file.

Then it is recommended that you use the following code to run this method:

 public class WildcardFixed { void foo(List<?> i) { fooHelper(i); } // Helper method created so that the wildcard can be captured // through type inference. private <T> void fooHelper(List<T> l) { l.set(0, l.get(0)); } } 

I understand a little why this code works, in this code l.get guaranteed to be of type T, so it can be passed as an argument of type T.

I do not understand why you could not just use a method like this without an assistant:

 class GenericsTest { static <K> void bar(List<K> l) { l.set(0, l.get(l.size() - 1)); } public static void main(String[] args) { List<Integer> lst = Arrays.asList(1, 2, 3, 4); bar(lst); System.out.println(lst); // [4, 3, 2, 4] } } 

i.e. if you're going to use type inference, why not just use explicitly typed template generalizations and a helper function? Is there any advantage when using wildcards in this scenario? In which cases would you prefer to use a wildcard for a typed generic?

+5
source share
2 answers

First of all, note that void foo(List<?> i) and void <K> foo(List<K> i) both take the same set of arguments - unless you explicitly specify K in the case of a generic method, any argument that can be transferred to one signature, functions can be transferred to another, and vice versa. Thus, for "external code", both signatures are equally useful.

Given that they are equivalent, one that has fewer type parameters is simpler and should always be preferred. When you present an open API for external code, you should always present it in its simplest form, having only the minimum type necessary for security. The List<?> Type is enough to express that it can accept any List type, so this is what we should use.

We use a parameter of type K inside to solve some problems with generics, but this is just an unsuccessful internal implementation detail that does not need to take care of the external code. Therefore, we must hide this ugliness and wrap it with a more beautiful function signature when creating a public API.

In addition to abstraction, another reason you might need a specific signature is because it overrides the method of the superclass that has that signature. You cannot add type parameters when overriding.

+1
source

I think the example you mentioned in the tutorial applies to processing cases where you need to accept parameters with unrelated wildcards <?> . This may be the case when interacting with legacy code or extending an existing class with such a wildcard type.

You will mainly use things like <T extends U> or <? extends T> <? extends T> when you need flexibility but don’t want to sacrifice type checking.

0
source

Source: https://habr.com/ru/post/1202375/


All Articles