2

I find this completely baffling. For some reason, javac appears to treat references with unspecified generics and <?> differently in regards to type safety. Does anyone have an idea of why this is (Java 1.6)?

public abstract class ClassWithGenericMember<GenMember> {

  GenMember getMember() {
    return (GenMember) null;
  }

  <GenReturn> GenReturn handle(Handler<GenReturn> handler) {
    return handler.handle();
  }

  interface Handler<GenHandled> {
    public GenHandled handle();
  }

  static class ClassWithMember extends ClassWithGenericMember<MemberType> {
  }

  static class MemberType {
  }

  static class HandledType {
  }

  public static void main(String[] argv) {
    HandledType handled = null;
    Handler<HandledType> handler = new Handler<HandledType>(){
      public HandledType handle() {
        return (HandledType) null;
      }
    };

    ClassWithMember concrete = new ClassWithMember();
    ClassWithGenericMember<?> bracket = concrete;
    ClassWithGenericMember noBracket = concrete;

    handled = concrete.handle(handler); //compiles
    handled = bracket.handle(handler); //compiles
    handled = noBracket.handle(handler); //fails:
    /*
ClassWithGenericMember.java:38: incompatible types
found   : java.lang.Object
required: ClassWithGenericMember.HandledType
    handled = noBracket.handle(handler);
                              ^
Note: ClassWithGenericMember.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
    */
  }
}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Zalgef
  • 23
  • 2
  • 3
    This is totally as it should be. The raw types (without generics) eliminate _all_ type safety that generics would provide, whereas `?` represents a fixed but unknown generic type that can be reasoned about. – Louis Wasserman May 15 '13 at 23:19
  • But the call does not in any way use the class's generic member type. The compiler error is for the generic method, which is a different generic specified by the handler. – Zalgef May 15 '13 at 23:23

1 Answers1

3

As odd as it may seem, when you use a raw type, all the generic typing (all of them, not just the parameterized types of the class) is removed from the declarations within the class (JLS 4.8):

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.

When you use ClassWithGenericMember noBracket ("raw type C"), then the type of noBracket.handle() is the "raw type that corresponds to the erasure of its type", i.e. Object handle(HandlerType handler). It doesn't matter that (as you say in the comments) the "call does not in any way use the class's generic member type".

Note that this only applies to the type of the constructors, instance methods, and non-static fields declared directly in the class (e.g. not class methods, static fields, inherited methods) and does not affect the declarations with the implementation (e.g. declarations in methods).

--

This question/answer has come up in a few times in SO like:

Community
  • 1
  • 1
Bert F
  • 85,407
  • 12
  • 106
  • 123