Why is this (incorrect) use of Java generics not compiling?

Possible duplicate:
Why is this common Java code not compiling?

Given the following code:

import java.util.Collections; import java.util.List; public class ComeGetSome { //TODO: private final Some<?> some = new Some(); private final Some some = new Some(); public static void main(String[] args) { new ComeGetSome().dude(); } public void dude() { for (String str : some.getSomeStrings()) { //FIXME: does not compile! System.out.println(str); } } } class Some<T> { public List<String> getSomeStrings() { return Collections.<String> emptyList(); } } 

It does not compile because some.getSomeStrings() returns a raw List . But the signature of the method indicates that it returns a List<String> !

Somehow this is due to the fact that Some has a type declaration, but is referred to as a raw type. Using a link to Some<?> Fixes the problem. But the method has nothing to do with the type declaration in the class!

Why does the compiler behave like this?

+4
source share
2 answers

If you use an unprocessed (untyped) instance of a universal class, then it is considered to be completely raw, that is, all information about the general type is ignored, even if the ignored type is not associated with a typical type that was omitted.

That is why it is ...

 private final Some<?> some = new Some(); 

... fixes the error - it uses a typed version of Some (albeit a wildcard)

+4
source

Well, first of all, it makes no sense to declare a Some generic class unless you actually use a parameter of a certain type. And this is actually a source of problems. You instantiate a member field of this type in this fasion:

 private final Some some = new Some(); 

This raw type declaration is a generic type in which type parameters are not specified. This actually means not only that its own type parameter T discarded, but all common types used in these methods of the class are ignored, including the <String> parameter in the public List<String> getSomeStrings() .

So the solution is actually simple, quoting Joshua Bloch:

Do not use source types in new code.

To make this specific, the fix you suggested is:

 private final Some<?> some = new Some(); 

fixes the problem, but actually generates a compiler warning (unverified operation). A clean way would be to either declare the actual type parameter on the right side of the instruction, for example:

 private final Some<String> some = new Some<String>(); 

The type that you put in the generic parameter can be any type you want, depending on how you intend to use the Some class. But if you really don't need a type parameter, just delete it and the code will work fine:

 class Some { ... } 

Note: there is no need to explicitly declare a String parameter in this code:

 return Collections.<String> emptyList(); 

The compiler infers the generic type from the method signature, so you can simplify the code:

 public List<String> getSomeStrings() { return Collections.emptyList(); } 

Edit : Speaking of Joshua Bloch, there is a good Java puzzle regarding this factual issue in his talk on Google I / O 2011 at: http://www.youtube.com/watch?v=wbp-3BJWsU8&t=36m04s

+1
source

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


All Articles