Параметризованный тип.getRawType() возвращает тип j.l.r., а не класс?

#java #reflection

#java #отражение

Вопрос:

 ParameterizedType parameterized =
    (ParameterizedType) List.class.getMethod("iterator").getGenericReturnType();
Type raw = parameterized.getRawType();
  

ParameterizedType#getRawType() возвращает a Type , а не a Class<?> (хотя я понимаю, что это java.lang.Class теперь реализуется Type ). Есть ли веская причина, по которой getRawType() не объявляется возвращаемый тип как Class<?> ? Существуют ли крайние случаи, когда getRawType() результатом может быть не Class<?> ?

С этим и так достаточно хлопот, чтобы работать j.l.r.Type ; похоже, это тот случай, когда они могли бы избавить нас от одного разочарования.

Комментарии:

1. Этот поток velocityreviews.com/forums/… указывает, что возвращаемый тип всегда является классом, но я также ищу дополнительные мнения по этому поводу.

2. Для меня это выглядит как базовое разделение интерфейса и реализации. Javadoc указан Class как единственная реализация Type , но JRE содержит пару десятков защищенных классов, которые также реализуют Type .

3. Я бы согласился с аргументом разделения интерфейса и реализации, если бы Type это не был интерфейс-маркер. Чтобы делать то, что я хотел бы делать с a Type , я должен проверить, является ли это a Class<?> , ParameterizedType WildcardType GenericArrayType ,,, и тому подобное. Если бы я мог выполнить то, что я хотел, с произвольным объектом просто как Type , для меня было бы не так важно, был ли класс выполнения результата java.lang.Class или что у вас есть.

4. @skaffman: Это только показывает, что Class может быть единственной общедоступной и документированной реализацией в jdk.

Ответ №1:

Он должен возвращать Class объект, другого способа нет.

Почему? Кто знает, может быть, какой-то идеалистический уклон. Если бы он вернул Class , это было бы единственным появлением Class в новых Type интерфейсах.

Реальная проблема заключается в смешивании Class и Type . Ранее все типы были представлены в Class . Это уже было грязно, но все еще терпимо. Типов было не так уж много.

С новыми универсальными типами они должны были разработать более чистую Type иерархию, соответствующую спецификации, независимую от Class . Вместо этого они объединили Class с Type и создали еще больше беспорядка. Вся иерархия просто не имеет смысла. Любой новичок в этой теме и не знакомый с историей будет потрясен этой бессмыслицей.

Я бы не стал поддерживать дизайн Type на высоком уровне. стандарт. Например, ParameterizedType определяет equals() , но не hashCode() . Не существует способа, чтобы две реализации ParameterizedType работали в одной хэш-карте. И подстановочный знак тоже является типом? Черт возьми, нет.

И название метода getRawType() просто идиотское. Это не имеет ничего общего с raw type . Он должен быть четко назван getClassOrInterface() . Не будет ли это слишком подробным? Посмотрите getActualTypeArguments() тогда. (И да, он возвращает фактические аргументы! Не поддельные!)

Комментарии:

1. ParameterizedType это просто интерфейс, и он не определяет equals() или hashCode() . sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl кажется, определяет оба, по крайней мере, в Sun JRE 1.5.0_22. Вы всегда можете преобразовать ParameterizedTypeImpl файлы, полученные из reflection, в свою собственную реализацию ParameterizedType , если они ведут себя не так, как вы хотите.

2.@Andy Экземпляры классов, реализующих этот интерфейс, должны реализовывать метод equals(), который уравнивает любые два экземпляра, которые используют одно и то же объявление универсального типа и имеют одинаковые параметры типа. Исходный код, но без определения для hashCode() этого не очень полезно, если вы собираетесь использовать ParameterizedType экземпляры в Set . Решением могло бы быть обернуть все ParameterizedType s в реализацию пользовательским hashCode() методом, чтобы убедиться, что equal записи возвращают то же самое hashCode .

Ответ №2:

Я думал об этом, и у меня есть догадка. Возможно, они хотели оставить открытой возможность для подобных безумств в будущем:

 public class Z<L extends List<?>> {
    L<Double> test;
}
  

Это не легальный Java-код, но я думаю, ясно, что это будет означать; new Z<ArrayList<?>>().test будет иметь тип ArrayList<Double> .

Если бы это было законно, ((ParameterizedType) test.getGenericType()).getRawType() вернуло бы TypeVariable .

Ответ №3:

Реализация Sun ParameterizedType определила getRawType() метод для возврата Class<?> . Таким образом, он явно возвращает только Class<?>

Однако в моем пути к классу есть еще несколько реализаций ParameterizedType — из hibernate-validator, из aspectj, hibernate-annotations, jaxb. Некоторые из них возвращают Class<?> , некоторые — Type . Хотя я не знаю, как они используются.

Комментарии:

1.да? Он возвращает Type download.oracle.com/javase/6/docs/api/java/lang/reflect /… редактировать ok, который только class реализует.

2. Я сказал о реализации. sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl

3. Странная вещь в том, что ParameterizedTypeImpl.getOwnerType() возвращает Type !

Ответ №4:

Иерархия интерфейса типов может использоваться не только в reflection api. Например, библиотека генерации кода может определять пользовательские реализации. Сам JDK 8 имеет 3 разные реализации WildcardType . Если ParameterizedType.getRawType() вернул экземпляр класса, то вам нужно было бы иметь возможность создавать экземпляр класса в любое время, когда вы захотите.

Класс — это очень глубоко внедренный в JVM тип, который имеет привязки обратно к встроенной памяти, управляемой пользователем. Чтобы создать экземпляр класса, у вас должен быть байтовый код, который определяет класс. Но в случае библиотеки генерации кода байтовый код еще даже не существует. Если бы они потребовали, чтобы ParameterizedType возвращал класс, это ограничило бы применимость иерархии интерфейса типа только api отражения.

Это может показаться несущественным, но ни то, ни другое не является актерским составом.

ParameterizedType.getOwnerType() возвращает тип, поскольку он сам может быть либо классом, либо другим параметризованным типом. Теоретически это может возвращать переменную типа, поскольку для Java допустимо следующее:

 <M extends Map<?,?>> M.Entry<?,?> first(M map) { ... }
  

Однако он скомпилирован как статическая ссылка на удаление переменной типа, в этом случае M.Entry будет скомпилирован как Map.Entry . Вместо переменной типа вызов getOwnerType() из reflection api будет классом, по крайней мере, согласно моим тестам.