Подтвердить что ты не робот

Java: getClass() ограниченного типа

Я заметил что-то, в то время как я искал дженерики. В приведенном ниже примере doStuff1 компилируется, но doStuff2 не выполняет:

public <T extends Foo> void doStuff1(T value) {
    Class<? extends Foo> theClass = value.getClass();
}

public <T extends Foo> void doStuff2(T value) {
    Class<? extends T> theClass = value.getClass();
}

Итак, я просмотрел документацию для Object.getClass() и нашел это:

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

Мне было немного любопытно. Почему getClass() разработан таким образом? Я могу понять, как преобразовать типы в свои исходные классы, если это применимо, но я не вижу очевидной причины, по которой они обязательно должны были бы заставить его также убить T. Есть ли конкретная причина, по которой он также избавляется от этого, или это просто общий "давайте просто избавиться от всего, потому что это проще, кто когда-нибудь понадобится" в любом случае?

4b9b3361

Ответ 1

Если getClass() возвращает Class<? extends X>, ничего плохого не может произойти; на самом деле это поможет многим случаям использования.

Единственная проблема - это не теоретически правильно. если объект является ArrayList<String>, его class не может быть Class<ArrayList<String>> - такого класса нет, существует только Class<ArrayList>.

На самом деле это не связано с стиранием. Если в один прекрасный день Java получит полный тип reified, getClass() должен возвращать Class<? extends |X|>; однако должен быть новый метод, например getType(), который может вернуть более подробный Type<? extends X>. (хотя getType может конфликтовать с множеством существующих классов со своими методами getType)

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

static <X> Class<? extends X> myGetClass(X x){ ... }

но понятно, что они не поместили бы этот хак в стандартную библиотеку.

Ответ 2

Вы не можете быть уверены, что value на самом деле является T, он также может быть подклассом T. getClass() возвращает "реальный" класс value, но вы не можете быть уверены, что T, поэтому он должен читать Class<? extends T>! Это не имеет никакого отношения к тому, что T является параметром типа.

[Изменить] Хорошо, не совсем, я не понял, что метод2 не компилируется. Я думаю, что это проблема с компилятором. Я получаю

Type mismatch: cannot convert from Class<capture#1-of ? extends Foo> to Class<? extends T>

Я думаю, что компилятор понимает только, что объект T является Foo. Компилятор должен знать, что getClass() возвращает некоторый Class<? extends T>, но только, кажется, знает, что возвращает некоторый Class<? extends Foo>. Означает ли это, что Foo является erasure of the static type T?

Ответ 3

Очень хороший вопрос.

Вся проблема с дженериками java заключается в том, что они не существовали на старых виртуальных машинах. Sun решила добавить общую поддержку, хотя существующая виртуальная машина не поддерживала их.

Это может быть большой проблемой, потому что, когда они выпустят новую Java, старые виртуальные машины НЕ смогут запускать какой-либо код, написанный для новейшей версии Java, и они не могли бы использовать устаревшую поддержку старого виртуального машины.

Итак, они решили внедрить дженерики таким образом, чтобы их можно было запускать на старых виртуальных машинах. Они решили УДАЛИТЬ ИХ ОТ БЫСТРОГО.

Итак, вся история о поддержке со стороны jvms.

РЕДАКТОР: Но это, возможно, не имеет никакого отношения к вопросу. См. Ответ kutschkem!

Кроме того, я предполагаю, что во втором случае литье Class<? extends List> - Class<? extends T>. Это приведение возможно только в том случае, если List непосредственно расширяет T (поскольку это неявный перевод). Но это наоборот, T расширяет список.

Я думаю, что это то же самое, что сказать:

String s = "s";
Object o = s;

В приведенном выше случае литье возможно, потому что String расширяет Object. Для обратного вам понадобится явный листинг.

Если вы добавляете явное преобразование в свою программу, оно компилируется:

public <T extends List> void doStuff2(T value) {
   Class<? extends T> theClass = (Class<? extends T>) value.getClass();
}

Ответ 4

Инструменты guava reflection предоставляют некоторые инструменты для работы с этим ограничением и помогают вам получить тип (таким образом, класс) ограниченного типа.

Как сказано в первой строке этой страницы объяснения:

Из-за стирания типа вы не можете передавать общие объекты класса в runtime - вы могли бы бросить их и притвориться, что они общие, но на самом деле это не так.

Guava предоставляет TypeToken, который использует фокусы на основе отражений, чтобы вы можете манипулировать и запрашивать общие типы, даже во время выполнения.

Идея состоит в том, чтобы получить (или создать) TypeToken ограниченного типа, а затем вы можете запросить его тип.

Я понял, что этот ответ немного не соответствует теме, потому что он не отвечает на вопрос напрямую (часть "почему" ), но он может исправить не компилируемый метод, это может привести к тому, что код сможет решить вопрос и дать вам ответ на причину, и, безусловно, поможет другим людям, ищущим "Java: - как -getClass() ограниченного типа"