1

Given this simple class:

    import java.util.Collection;

    public class GenericTest<T> {
      public Collection<String> getKeys() {
        return null;
      }
      public void copy(GenericTest a_from) {
        for (String x : a_from.getKeys()) {

        }
      }
    }

I am getting the following compile error, but don't understand why.

    error: incompatible types
    for (String x : a_from.getKeys()) {
      required: String
      found:    Object

The error goes away if I change the parameter to the copy() method to GenericTest<T>, but that is not what I want. The copy() method is valid on any type of GenericTest, not just GenericTest<T>.

Mitch
  • 989
  • 1
  • 9
  • 25

3 Answers3

2

This is not how you create a generic class. If you use a raw type of your generic class, then all the parameterized type used inside the class, loose their type information. So, for GenericTest raw type, the getKeys() method signature changes to:

public Collection getKeys() {
    return null;
}

So, if you iterate over getKeys() method of GenericTest raw type, you will get Object, and not String, which I don't see why you expect.

From JLS Section 4.8 - Raw Types:

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.


You should really use GenericTest<T> as parameter type in your method, instead of raw type. And change the return type of getKeys to Collection<T>.

Change your class to:

public class GenericTest<T> {
    public Collection<T> getKeys() {
      return null;
    }
    public void copy(GenericTest<T> a_from) {
      for (T x : a_from.getKeys()) {

      }
   }
}

The type T is infered from the parameterized type you create for this generic class. For GenericTest<String>, T will be infered as String, in your class.


Reference:

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • But the getKeys() method really does return a Collection of String, not T. (The example here is pared down from my actual class, just to show the error condition.) – Mitch Aug 16 '13 at 15:52
  • @Mitch. In that case, use `GenericTest` as method parameter. But I don't see any reason you would make your class generic at all. – Rohit Jain Aug 16 '13 at 15:54
  • My confusion was that the compiler changes the return type of the getKeys() method, even though I want it to be String. Just goes to show there is always more to learn! – Mitch Aug 16 '13 at 16:04
  • @Mitch. Hmm. You should go through the link I posted at the end of my answer. That is the best generics reference you will get. – Rohit Jain Aug 16 '13 at 16:04
1

you might wanted to write

public void copy(GenericTest<String> a_from) {

or

public void copy(GenericTest<T> a_from) {

this looked odd to me too, explanation here (this is a duplicate): Why won't this generic java code compile?

Community
  • 1
  • 1
Zoltán Haindrich
  • 1,788
  • 11
  • 20
  • See my original post. The copy() method is valid for any type of GenericTest, not just those of type T. – Mitch Aug 16 '13 at 15:53
1

Perhaps you want:

public void copy(GenericTest<?> a_from) {
    for (String x : a_from.getKeys()) {

    }
}

This would accept any generic type, as per your request.

I modified your class, just so it wouldn't return a null collection, and I made the for loop print the strings read.
This is the result, which compiles (and runs) just fine.

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

public class GenericTest<T> {
    public Collection<String> getKeys() {
        ArrayList<String> a = new ArrayList<String>();
        a.add("1");
        a.add("2");
        a.add("3");
        return a;
    }

    public void copy(GenericTest<?> a_from) {
        for (String x : a_from.getKeys()) {
            System.out.println(x);
        }
    }

    public static void testIt() {
        GenericTest<Integer> gti = new GenericTest<Integer>();
        GenericTest<String> gts = new GenericTest<String>();

        gts.copy(gti);
    }
}
afsantos
  • 5,178
  • 4
  • 30
  • 54