Is it possible to implement a method with the signature List <Class <? extends annotation >> in Java?

The problem is the general limitation:

public List<Class<? extends Annotation>> getAnnotations() { return new ArrayList<>(Arrays.asList(Override.class)); } 

Real return type ArrayList<Class<Override>>
Method expects List<Class<? extends Annotation>> List<Class<? extends Annotation>>

Class<Override> is a subtype of Class<? extends Annotation> Class<? extends Annotation>
Class<? extends Annotation> c = Override.class; // allowed

ArrayList is a subtype of a List if the item types are the same:
List<? extends Number> l = new ArrayList<Integer>(); // allowed

However, this is not allowed:

 List<Class<? extends Annotation>> l = Arrays.asList(Override.class); List<Class<? extends Annotation>> l = new ArrayList<>(Arrays.asList(Override.class)); 

Is this possible, or are Class wildcards broken?

+5
source share
2 answers

I would suggest that this is due to the nature of output like jdk 1.7.

As you already know, the Arrays.asList(T ... elems) is common, but we rarely specify the type of parameter we would like to work with, and thus we rely on a compiler type inference function.

So, when the compiler sees the operator Arrays.asList(Override.class) , it will infer that the type parameter for the method should be replaced with Class<Override> , that is, we will have a version of the method in this form:

 public List<Class<Override>> asList(Class<Override> ... elems) 

However, if you explicitly set the type parameter for the method

 List<Class<? extends Annotation>> l = Arrays.<Class<? extends Annotation>>asList(Override.class); 

then the compiler really finds out what needs to be replaced with the type parameter, and then the version of the .asList() method will be:

 public List<? extends Annotation> asList(Class<? extends Annotation> ... elems) 

Now this compiles fine since Class<? extends Annotation> Class<? extends Annotation> compatible with Class<Override> . In Java8, the type inference function is further improved, so you do not need to explicitly set the type parameter for the .asList() method.

However, an interesting question relates more to

Why is List<Class<Override>> not compatible with List<Class<? extends Annotation>> List<Class<? extends Annotation>> ?

java.lang.Class is final , which will help answer the following two questions, a combination of which will answer the above question. :)

So,

  • What does List<Class<Override>> mean?

List<Class<Override>> means that we can only add instances of Class<Override> and nothing else to the list. This is great, knowing that we cannot even add subclasses of Class<Override> , since the type is Class final .

  • What does List<Class<? extends Annotation>> mean List<Class<? extends Annotation>> List<Class<? extends Annotation>> ?

This List type represents a whole family of class lists, all of which are subclasses of the Annotation type, which means that we can successfully add any type of annotation (for example, SuppressWarnings.class , Override.class , Documented.class , etc.).

Suppose the following example was actually correct:

 List<Class<Override>> overrides = Arrays.asList(Override.class); List<Class<? extends Annotation>> annotations = new ArrayList<>(); annotations = overrides; annotations.add(SuppressWarnings.class); //HUGE PROBLEM annotations.add(Documented.class); //ANOTHER HUGE PROBLEM 

Two huge problems arise because we are trying to add some non- Override instances to overrides , which is very wrong.

We have a smart enough compiler that can actually detect such possible problems, and throwing a compile-time error is a way to stop us from doing this.

Additional Information:

+2
source

ArrayList is a subtype of List if the item types match:

 List<? extends Number> l = new ArrayList<Integer>(); // allowed 

Yes, but in your example the element types do not match:

 List<Class<? extends Annotation>> l = new ArrayList<Class<Override>>(); 

Of course, Class<Override> is a subtype of Class<? extends Annotation> Class<? extends Annotation> , but just like List<String> not a subtype of List<Object> , List<Class<Override>> not a subtype of List<Class<? extends Annotation>> List<Class<? extends Annotation>> . However, this would be a subtype of List<? extends Class<? extends Annotation>> List<? extends Class<? extends Annotation>> List<? extends Class<? extends Annotation>> .

However, the reason your code does not compile is because in Java 7 type inference does not take into account the type of the returned method when printing the type of the expression of the return statement, so by default it refers to the most specific type that can be assigned

 Arrays.asList(Override.class) 

not realizing that the return statement will only compile with a more flexible type (Java 8 type output is smarter, by the way). One workaround is to explain an argument of the type:

 Arrays.<Class<? extends Annotation>(Override.class); 

or give output like Java 7 with a hint by first assigning a local variable:

 List<Class<? extends Annotation>> list = Arrays.asList(Override.class); return list; 

or change the type of the returned method to

 List<? extends Class<? extends Annotation>> getAnnotations() 

therefore, the intended type does not matter.

+1
source

All Articles