14

I would expect that from the aspect of compile time as well as from the aspect of runtime it wouldn't be a problem for .getClass() to provide a correctly-typed return value.

But I must be wrong.

public class _GetClassGenerics2 {

  static class MyClass {
  }

  public static void main(String[] args) {
    MyClass myInstance = new MyClass();
    // here it works
    Class<? extends MyClass> type = myInstance.getClass();

    myMethod(myInstance);
  }

  public static <T extends MyClass> void myMethod(T instance) {
    Class<? extends T> type = instance.getClass();
// java.lang.RuntimeException: Uncompilable source code - incompatible types
//  required: java.lang.Class<? extends T>
//  found:    java.lang.Class<capture#1 of ? extends _GetClassGenerics2.MyClass>
  }

}

EDIT: It doesn't work with Class<T> and Class<? super T> either.

java.is.for.desktop
  • 10,748
  • 12
  • 69
  • 103

4 Answers4

6

java.lang.Class does not represent a type (use java.lang.reflect.Type for that). If T, were say ArrayList<String> then it makes no sense for there to be a Class<ArrayList<String>>.

It's worth noting that in this particular case there is no need for the method to be generic.

public static <T extends MyClass> void myMethod(T instance) {

Is equivalent to:

public static void myMethod(MyClass instance) {
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
4

As per the Javadoc of the getClass method:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment

Here, the value for |X| in your code snippet is MyClass, hence instance.getClass() is assignable to only Class<? extends MyClass> or Class<?>.

The reason for this specific wording is because when you say that for this variable having type T where <T extends MyClass>, there can be multiple classes which extend MyClass and hence capable of satisfying the T extends MyClass criteria. Without runtime information there is no way of knowing which concrete implementation subclass of MyClass was passed in the method. Hence to provide a generic solution, it returns <? extends MyClass> since that would hold true for any subclass of MyClass irrespective of what class instance is passed in.

user11171
  • 3,821
  • 5
  • 26
  • 35
Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • 3
    What I don't get is why X is MyClass, not T. – Sergei Tachenov Dec 16 '10 at 15:35
  • 1
    @Sergey Tachenov: Because AFAICT, `|X|` is an erasure and hence can't be a parametrized type. If it were simply `T`, the Javadoc wouldn't have explicitly mentioned *erasure of static type*. – Sanjay T. Sharma Dec 16 '10 at 16:05
  • 3
    Oh, finally I got it. Language specification 4.6 explicitly says that "The erasure of a type variable (§4.4) is the erasure of its leftmost bound". Which is MyClass in our case. I don't see why they made it this way, though. – Sergei Tachenov Dec 16 '10 at 16:34
  • @Sergey: That's because when you say that for this variable having type `T` where ``, there can be multiple classes which extend `MyClass` and hence capable of satisfying the `T extends MyClass` criteria. Without runtime information there is *no* way of knowing which concrete implementation subclass of `MyClass` was passed in the method. Hence to provide a generic solution, it returns ` extends MyClass>` since that would hold true for any subclass of MyClass irrespective of what class instance is passed in. Hope that clarifies it. – Sanjay T. Sharma Dec 16 '10 at 16:46
  • 1
    So basically it is because there is no way to ensure at compile time that the cast is correct since the actual type for T is unknown? I think I got it now. By the way, please fix your answer, there is some code missing in the edit. – Sergei Tachenov Dec 16 '10 at 17:11
  • @Sergey: Correct; this is very similar to the reason why you can't add items to a bounded `List` since at runtime the `List` is capable of holding items of any of the subclasses of `SomeClass`. BTW, fixed the answer. – Sanjay T. Sharma Dec 16 '10 at 19:14
  • @SanjayT.Sharma: "The reason for this specific wording is because when you say that for this variable having type T where , there can be multiple classes which extend MyClass and hence capable of satisfying the T extends MyClass criteria." No. This doesn't make any sense. The method is generic over `T`, but for any given choice of `T`, there is a specific type -- `T`, and the question is why `.getClass()` on this does not return `Class extends T>`. – newacct May 26 '16 at 00:22
  • @SanjayT.Sharma: "this is very similar to the reason why you can't add items to a bounded List since at runtime the List is capable of holding items of any of the subclasses of SomeClass." You *can* add items of type `T` to a `List`. – newacct May 26 '16 at 00:22
2

Java does not support a generic type of <this> e.g.

Object could implement

class Object {
    Class<this> getClass()
}

But there is no way for getClass() to express that it will return a type which is the class of the object. The compiler has no native understand of what this method does either.

IMHO, This behaviour should have been supported.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Good answer. Note however that you kind of explain the second step before the first. The immediate problem is that Object.getClass() does not return the "right" type. You explain *why* it does not do this. – sleske Dec 16 '10 at 12:58
  • Could you point to any resource that explains what exactly is ``? I can't find it in the language specification and can't compile something like that. – Sergei Tachenov Dec 16 '10 at 19:07
  • Sorry, Java **does** support the "this-type" feature? It would be great, but I guess you mean **does not**, right? Because `Class getSomething()` wouldn't work. – java.is.for.desktop Dec 16 '10 at 19:14
  • @java.is.for.desktop thank you for picking that up, fixed it now. – Peter Lawrey Dec 16 '10 at 20:35
  • "But there is no way for getClass() to express that it will return a type which is the class of the object." According to that logic, there is also no way for `getClass()` "to express" that it returns `Class extends (the erasure of the static type of this)>`, but that's actually what it returns. – newacct May 26 '16 at 00:24
  • @newacct that is because it's behaviour is defined in JLS 15.8.2 from Java 6. which "overrides" the type used in the return Java 5.0 didn't have this behaviour. – Peter Lawrey May 26 '16 at 14:52
  • @PeterLawrey: Right, so the OP's question is why can't it be defined another way. – newacct May 26 '16 at 18:54
  • @newacct there is no syntax which supports what the OP wants. – Peter Lawrey May 26 '16 at 18:57
  • @PeterLawrey: There is no syntax which supports how it works right now either. But it works, because it's in the JLS. That's my point. What the OP wants could be in the JLS also. It's not, and why is the OP's question. – newacct May 26 '16 at 21:48
0

Instead of

Class<? extends T> type = instance.getClass();

you need to use

Class<? extends MyClass> type = instance.getClass();

You cannot directly use the T here.

The reason is the method signature of Object.getClass() (which you are invoking). It's:

public final Class<? extends Object> getClass()

So you are trying to convert from Class<? extends Object> to Class<? extends T>, which is not allowed (because you are down-casting). It is allowed using an explicit cast:

Class<? extends T> type = (Class<? extends T>) instance.getClass();

will work (though it generates a type safety warning).

sleske
  • 81,358
  • 34
  • 189
  • 227
  • This won't be a problem, but the question is why. Is there a reason? At this point we know that instance must be either of class T (whatever that is) or of its subclass. So why `? extends T` isn't allowed? – Sergei Tachenov Dec 16 '10 at 12:43
  • @Sergey: The reason is that, while we know that `instance` must be of type `T` or subclass, `Object.getClass()` does not take that into account. See my edited answer. – sleske Dec 16 '10 at 12:57
  • 1
    I still don't get it. The javadoc says "The actual result type is Class extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called." In this case X is T. Are you trying to say that X is actually Object? And that's because it is type parameter (as it works outside of the generic method)? – Sergei Tachenov Dec 16 '10 at 13:12
  • @Sergey: The compiler does not care about the "actual result type" of `getClass()`. The compiler does static type-checking, therefore only cares about the method signature. And the signature says `Class extends Object>`. If you *know* that the result type is really a subclass of what the method signature says, you can cast it, but you must cast explicitly. – sleske Dec 16 '10 at 16:20
  • By "actual result type" Javadoc means "actual result type of a signature" or "static return type" because it also mentions that no explicit cast is required (to the actual result type), so it's not really an answer. The real answer is mentioned in the comments to Sanjay's answer. – Sergei Tachenov Dec 16 '10 at 16:37
  • @Sergey: Ah, I see that the javadocs were expanded for JDK 1.6; sorry for the confusion. – sleske Dec 16 '10 at 18:48
  • According to your explanation, `Class extends MyClass>` shouldn't work either, because `Class extends Object>` cannot be implicitly converted to `Class extends MyClass>`. – newacct May 26 '16 at 00:26