6

I don't understand why this confuses the compiler. I'm using the generic type T to hold an object that's not related to the put and get methods. I always thought GenericClass and GenericClass<Object> were functionally identical, but I must be mistaken. When compiling the DoesntWork class I get incompatible types - required: String - found: Object. The Works class does what I expect. What's going on here?

public class GenericClass<T> {
    public <V> void put(Class<V> key, V value) {
        // put into map
    }

    public <V> V get(Class<V> key) {
        // get from map
        return null;
    }

    public static class DoesntWork {
        public DoesntWork() {
            GenericClass genericClass = new GenericClass();
            String s = genericClass.get(String.class);
        }
    }

    public static class Works {
        public Works() {
            GenericClass<Object> genericClass = new GenericClass<Object>();
            String s = genericClass.get(String.class);
        }
    }
}
Ryan J
  • 2,502
  • 5
  • 31
  • 41

2 Answers2

8

The thing about how raw types work -- generic types that you've left out the arguments for -- is that all generics for them and their methods are erased as well. So for a raw GenericClass, the get and put methods also lose their generics.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • The use of raw types is, essentially, an indication that you're using legacy, pre-generics code, for the purposes of compatibility; most of the weirdnesses around raw types are hacks for compatibility purposes. [JLS 4.8](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8) has more details. – Louis Wasserman Mar 31 '13 at 22:33
  • 2
    But for an example of why this specific behavior is necessary, consider the following example: legacy code built with Java 1.4 has to get the same behavior from `List` from Java 1.4 -- pre-generics -- and from `List` from Java 5, which was rewritten to introduce generics. It's awkward, but necessary for backwards compatibility -- but it should never be an issue for new code, since new code should basically never use raw types. – Louis Wasserman Mar 31 '13 at 22:35
  • "new code should basically never use raw types" - I can agree with that. – John Dvorak Mar 31 '13 at 22:37
  • 1
    So if I don't know the type when working on an object, I should use `?`? Ex: Vehicle with a washing decorator should use `wash(Vehicle> vehicle)` instead of `wash(Vehicle vehicle)`? – Ryan J Mar 31 '13 at 22:53
1

This is because when you work with a generic class without the extra type information you work with what is sometimes called the degenerate form of the class. The degenerate form has ALL generic type information removed.

Essentially - your class becomes something like:

public class GenericClass {
    public void put(Class key, Object value) {
        // put into map
    }

    public Object get(Class key) {
        // get from map
        return null;
    }

    ...
}

The compiler response you are seeing is therefore expected behaviour.

It's mentioned in a Java Puzzlers.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213