4

Possible Duplicate:
Why won't this generic java code compile?

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 method signature specify that it returns a List<String> !

Somehow it relates to the fact that Some has a type declaration but is referenced as a raw type. Using a reference to Some<?> fixes the problem. But the method has nothing to do with the type declaration on the class!

Why does the compiler behave like that?

Community
  • 1
  • 1
Крис
  • 1,190
  • 2
  • 11
  • 17

2 Answers2

4

If you use a raw (untyped) instance of a generic class, then it is treated as being completely raw, ie all generic type info is ignored, even if the type being ignored is not related to the generic type that has been omitted.

That's why this...

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

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

Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

Well, first of all, it doesn't make sense to declare the class Some generic if you don't actually use the defined type parameter. And that's actually the source of problems. You instantiate the member field of that type in this fasion:

private final Some some = new Some();

This is a declaration of a raw type - that is a generic type with it's type parameters left out. This actually means not only that it's own type parameter T is thrown away, but also all of the generic types used in that class's methods are ignored, including the <String> parameter in public List<String> getSomeStrings().

So the solution is actually simple, quoting Joshua Bloch:

Don’t use raw types in new code.

To make this concrete, the fix you suggested:

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

fixes the problem, but actually generates a compiler warning (unchecked operation). The clean way would be either declaring an actual type parameter on the right side of the statement, such as:

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

The type you put in the generic parameter can by any type you want, it depends on the way in which you intend to use the Some class. But if you actually don't need the type parameter, simply remove it and the code will work just fine:

class Some { ... }

A side note: it's not necessary to explicitly declare the 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 to this:

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

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

Natix
  • 14,017
  • 7
  • 54
  • 69