11

Possible Duplicate:
Generics in for each loop problem if instance does not have generic type assigned

Could someone clarify why iterate1() is not accepted by compiler (Java 1.6)? I do not see why iterate2() and iterate3() are much better.

import java.util.Collection;
import java.util.HashSet;

public class Test<T> {

    public Collection<String> getCollection() {
        return new HashSet<String>();
    }

    public void iterate1(Test test) {
        for (String s : test.getCollection()) {
            // ...
        }
    }

    public void iterate2(Test test) {
        Collection<String> c = test.getCollection();
        for (String s : c) {
            // ...
        }
    }

    public void iterate3(Test<?> test) {
        for (String s : test.getCollection()) {
            // ...
        }
    }


}

Compiler output:

$ javac Test.java
Test.java:11: incompatible types
found   : java.lang.Object
required: java.lang.String
  for (String s : test.getCollection()) {
                                              ^
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
Community
  • 1
  • 1
Alexander Pavlov
  • 2,264
  • 18
  • 25
  • Should `public Collection getCollection()` be `public Collection getCollection()`? I don't have my compiler available ATM to test the difference between these two. – Code-Apprentice Oct 02 '12 at 15:40
  • 4
    Answered in a discussion here - http://stackoverflow.com/questions/3316095/generics-in-for-each-loop-problem-if-instance-does-not-have-generic-type-assigne – Arham Oct 02 '12 at 15:45
  • No, my intention is to use Collection. I just do not understand why I cannot do it if I use non-typed reference to class. – Alexander Pavlov Oct 02 '12 at 15:45
  • @Arham Excellent. Key phrase from that question: "If you use the raw type (which you shouldn't) the compiler will erase **all** generic information for that instance even if it's not the type parameter T, to make it compatible with pre 1.5 code." – John Kugelman Oct 02 '12 at 15:48

1 Answers1

14

When you use a raw type (e.g. Test rather than Test<T>, the compiler treats it (JLS 4.8) as the erasure of that type (JLS 4.6) - that erases generics completely, whether they use the type parameter or not:

The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased.

Basically, the use of a raw type is treated by the compiler as an indication that you don't want that code to be aware of generics at all - so the method signature is erased to:

public Collection getCollection()

... hence the compile-time error, as the inferred element type is Object, as per JLS 14.14.2.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Now I understood difference between iterate1() and iterate3(). However, still do not understand difference between iterate1() and iterate2(). – Alexander Pavlov Oct 02 '12 at 16:01
  • @AlexanderPavlov: I'd expect `iterate2()` to give a warning about generic type safety, and Xlint to show an unchecked conversion from `Collection` to `Collection`. – Jon Skeet Oct 02 '12 at 16:08
  • Yes, it gives. But it is not clear why it gives error in iterate1() and warning in iterate2(). I use raw type in both methods so generics should be erased. – Alexander Pavlov Oct 03 '12 at 07:03
  • 2
    @AlexanderPavlov: Because in `iterate2()` you're explicitly assigning a raw type to a generic type (`Collection`) first. That's an unchecked conversion, but it's a valid one. – Jon Skeet Oct 03 '12 at 07:08