Java generics: почему someObject.getClass() не возвращает Class <? расширяет T>?

Я бы ожидал, что с точки зрения времени компиляции, а также с точки зрения времени выполнения для .getClass() не будет проблемой предоставить возвращаемое значение с правильной типизацией.

Но я должен быть неправ.

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: Он не работает с Class<T> и Class<? super T>.

Ответ 1

По Javadoc метода getClass:

Фактический тип результата Class<? extends |X|> где | X | это стирание статического типа выражения на который вызывается getClass. Для Например, в этом нет необходимости фрагмент кода

Здесь значение |X| в фрагменте кода MyClass, поэтому instance.getClass() можно присваивать только Class<? extends MyClass> или Class<?>.

Причина этой конкретной формулировки заключается в том, что, когда вы говорите, что для этой переменной, имеющей тип T, где <T extends MyClass>, могут быть несколько классов, которые расширяют MyClass и, следовательно, способны удовлетворять критериям T extends MyClass. Без информации о времени выполнения нет способа узнать, какой конкретный подкласс реализации MyClass был передан в методе. Следовательно, чтобы предоставить общее решение, он возвращает <? extends MyClass>, поскольку это верно для любого подкласса MyClass, независимо от того, какой экземпляр класса передан.

Ответ 2

java.lang.Class не представляет тип (используйте для этого java.lang.reflect.Type). Если T, скажем ArrayList<String>, то нет смысла, чтобы там было Class<ArrayList<String>>.

Стоит отметить, что в этом конкретном случае нет необходимости в том, чтобы метод был общим.

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

Является эквивалентным:

public static void myMethod(MyClass instance) {

Ответ 3

Java не поддерживает общий тип <this> например.

Объект может реализовать

class Object {
    Class<this> getClass()
}

Но для getClass() нет способа выразить, что он вернет тип, являющийся классом объекта. Компилятор не знает, что делает этот метод.

IMHO, Это поведение должно поддерживаться.

Ответ 4

Вместо

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

вам нужно использовать

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

Вы не можете напрямую использовать T здесь.

Причиной является сигнатура метода Object.getClass() (которую вы вызываете). Это:

public final Class<? extends Object> getClass()

Итак, вы пытаетесь преобразовать из Class<? extends Object> в Class<? extends T>, что недопустимо (потому что вы выполняете кастинг). Допускается использование явного приведения:

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

будет работать (хотя он генерирует предупреждение о безопасности типа).