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();
будет работать (хотя он генерирует предупреждение о безопасности типа).