«Обобщение стирается во время компиляции» — это общеизвестно (ну, в общем, параметры типа и аргументы стираются). Это происходит из-за «стирания типа» . Но неправильно, что все, что указано внутри символов <..>
стерто, как полагают многие разработчики. Смотрите код ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class ClassTest { public static void main(String[] args) throws Exception { ParameterizedType type = (ParameterizedType) Bar. class .getGenericSuperclass(); System.out.println(type.getActualTypeArguments()[ 0 ]); ParameterizedType fieldType = (ParameterizedType) Foo. class .getField( "children" ).getGenericType(); System.out.println(fieldType.getActualTypeArguments()[ 0 ]); ParameterizedType paramType = (ParameterizedType) Foo. class .getMethod( "foo" , List. class ) .getGenericParameterTypes()[ 0 ]; System.out.println(paramType.getActualTypeArguments()[ 0 ]); System.out.println(Foo. class .getTypeParameters()[ 0 ] .getBounds()[ 0 ]); } class Foo<E extends CharSequence> { public List<Bar> children = new ArrayList<Bar>(); public List<StringBuilder> foo(List<String> foo) { return null ; } public void bar(List<? extends String> param) {} } class Bar extends Foo<String> {} } |
Вы знаете, что это печатает?
класс java.lang.String
class ClassTest $ Bar
класс java.lang.String
класс java.lang.StringBuilder
интерфейс java.lang.CharSequence
Вы видите, что каждый отдельный аргумент типа сохраняется и доступен через отражение во время выполнения. Но тогда что такое «стирание типа»? Что-то должно быть стерто? Да. Фактически, все они, кроме структурных, все вышеизложенное связано со структурой классов, а не с потоком программы. Другими словами, метаданные об аргументах типа класса, его поле и методах сохраняются для доступа через отражение.
Остальное, однако, стирается. Например, следующий код:
1
2
3
4
5
|
List<String> list = new ArrayList<>(); Iterator<String> it = list.iterator(); while (it.hasNext()) { String s = it.next(); } |
будет фактически преобразовано в это (байт-код двух фрагментов идентичен):
1
2
3
4
5
|
List list = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { String s = (String) it.next(); } |
Таким образом, все аргументы типа, которые вы определили в теле ваших методов, будут удалены, и при необходимости будут добавлены приведения. Кроме того, если определен метод для приема List<T>
, этот T
будет преобразован в Object (или в его границу, если таковое объявлено. И вот почему вы не можете сделать new T()
(кстати, один открытый вопрос об этом стирании).
Итак, мы рассмотрели первые два пункта определения типа стирания . Третий — о мостовых методах. И я проиллюстрировал это с помощью этого вопроса stackoverflow (и ответ) .
Две «морали» всего этого. Во-первых, Java-дженерики сложны. Но вы можете использовать их, не понимая всех сложностей.
Во-вторых, не думайте, что вся информация о типах стерта — аргументы структурного типа присутствуют, поэтому используйте их, если необходимо (но не слишком полагайтесь на рефлексию).
Ссылка: | Об Java Generics и Erasure от нашего партнера JCG Божидара Божанова в техническом блоге Божо . |