4

Where is the difference between test1 and test2? Why compilation error in test1?

import java.util.ArrayList;
import java.util.Collection;

class MyType {

}

class MyClass<T> {
    private Collection<MyType> myTypes = new ArrayList<MyType>();
    private Collection<T> myTs = new ArrayList<T>();

    public Collection<MyType> getMyTypes() {
        return myTypes;
    }

    public Collection<T> getMyTs() {
        return myTs;
    }
}



public class TestSimple {

    public void test1() {    

        MyClass myClass = new MyClass();

        for (MyType myType : myClass.getMyTypes())  {

        }
    }

    public void test2() {            
        MyClass myClass = new MyClass();

        Collection<MyType> myTypes = myClass.getMyTypes();
        for (MyType myType : myTypes)  {

        }
    }

    public void test3() {
         MyClass<Long> myClass = new MyClass<Long>();

          for (Long myType : myClass.getMyTs())  {

          }        
     }

}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Alex
  • 967
  • 4
  • 15
  • 38

4 Answers4

4

If you define a generic constraint on a class, and then instantiate the class without providing any generic constraint (that is, you leave off the <> completely), then you've just stepped into the realm of Raw Types, where nothing is the same anymore.

According to the Java Language Spec:

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

According to Angelika Langer's excellent Java Generics FAQ,

Methods or constructors of a raw type have the signature that they would have after type erasure. A method or constructor call to a raw type generates an unchecked warning if the erasure changes the argument types.

So by constructing MyClass as a raw type (that is, as MyClass and not MyClass<?>), you have opted out of generics entirely, and the return type of getMyTypes() is now the raw type Collection, and not Collection<MyType>. As a result, you can't use the enhanced for syntax with type MyType, you'd have to use Object instead.

Of course, the better solution is just to use MyClass<?> (rather than just MyClass) when you mean a MyClass of an unknown parameterized type.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
2

I've isolated your problem to a smaller example, which I show below

import java.util.*;

public class TestSimple {

    static class MyClass<T> {
        private Collection<String> myTypes = new ArrayList<String>();

        public Collection<String> getMyTypes() {
            return myTypes;
        }
    }

    public void test1() {    
        MyClass myClass = new MyClass();
        for (String myType : myClass.getMyTypes())  {
        }
    }
}

My suspicion is that all type information is stripped off unless you specifically tell it otherwise. So my suggestion is to change your declaration:

before: MyClass myClass = new MyClass();
after:  MyClass<?> myClass = new MyClass();
corsiKa
  • 81,495
  • 25
  • 153
  • 204
2

Java is overly harsh on raw types. If you use raw type of generic class, all generic info in the class are ignored, even innocent ones that has nothing to do with type parameters of the class.

class A<T> implements List<String>

    Set<Integer> var;

If you use raw A, then it is treat as

class A implements List

    Set var;

This harsh treatment isn't necessary; they probably didn't think raw types deserve too much resource, so they took the easy route, erase every generic info from raw types indiscriminately.

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • +1 Interesting, I hadn't realized that about raw types. Should probably change `List` to `ArrayList` or another non-interface in the example. Also, it looks like a raw type implementing a generic interface in the same way *does* keep that type information. I wonder what the distinction was behind that decision. – Paul Bellora Sep 15 '11 at 04:33
  • 1
    for `A` to be raw, it has to be a generic class first. if `class A extends B`, `A` is not raw, because A is not even generic. – irreputable Sep 15 '11 at 06:29
0

In test2 since you haven't parameterized MyClass, getMyTypes() will effectively return Collection<Object>, which is not assignable to Collection<MyType>.

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • Why should it need to be parameterized for the method `public Collection getMyTypes()` to be paramerterized to `MyType`? For example, if you remove `` on `MyType` and remove `Collection getMyTs()` and remove `void test2()` and `void test3()` it compiles just fine. – corsiKa Sep 14 '11 at 21:29
  • @glowcoder yes, but I have "Collection myTs = new ArrayList();" also and it can't be removed – Alex Sep 14 '11 at 21:34
  • @Alex see my answer below. The problem still manifests even without those. – corsiKa Sep 14 '11 at 21:37