2013-10-08 5 views
8

В этом учебнике по reflection он гласит:дженерики удалены компилятором во время компиляции

[...] потому, что дженерики реализуются с помощью типа стирания, которая удаляет всю информацию, касающуюся общих типов во время компиляции

Насколько я знаю, генераторы используются так, что во время компиляции компилятор может проверить безопасность типа. то есть сбой быстрого подхода. Но ссылка упоминает, что стирание типа удаляет общую информацию во время компиляции.

+2

Во время компиляции, но после проверки. –

+2

Очевидно, что информация не удаляется до того, как компилятор будет создан с использованием информации. –

+0

Компилятор ничего не удаляет из вашего файла '.java', вместо этого он создает файл' .class' и не копирует комментарии, импорт или всю общую информацию (некоторые из них все еще существуют). Поскольку вы начинаете с нуля , вы не сможете их удалить. –

ответ

9

Заявление, которое вы цитируете, верно: компилятор использует информацию об общем типе внутри процесса компиляции, генерируя ошибки, связанные с типом, при обработке источников. Затем, как только проверка выполняется, компилятор генерирует стильный байт-код, при этом все ссылки на типовые типы заменяются на их стирание соответствующего типа.

Этот факт становится очевидным, когда вы смотрите на типы через отражение: все интерфейсы, классы и функции становятся неэквивалентными, причем все типы, привязанные к параметрам типового типа, заменяются не общим типом, основанным на ограничениях общего типа указанных в исходном коде. Хотя API-интерфейс отражения имеет положения для доступа к некоторой информации, связанной с генериками * во время выполнения, виртуальная машина не может проверить точный общий тип для совместимости при доступе к вашим классам через отражение.

Например, если вы сделаете член класса типа List<String> и попробуйте установить List<Integer> в него, компилятор будет жаловаться. Если вы попытаетесь сделать то же самое через отражение, однако, компилятор не собирается выяснить, и код будет терпеть неудачу во время выполнения таким же образом, что бы без дженериков:

class Test { 
    private List<String> myList; 
    public void setList(List<String> list) { 
     myList = list; 
    } 
    public void showLengths() { 
     for (String s : myList) { 
      System.out.println(s.length()); 
     } 
    } 
} 

... 

List<Integer> doesNotWork = new ArrayList<Integer>(); 
doesNotWork.add(1); 
doesNotWork.add(2); 
doesNotWork.add(3); 
Test tst = new Test(); 
tst.setList(doesNotWork); // <<== Will not compile 
Method setList = Test.class.getMethod("setList", List.class); 
setList.invoke(tst, doesNotWork); // <<== This will work; 
tst.showLengths(); // <<== However, this will produce a class cast exception 

Demo on ideone.


* См this answer подробную информацию о получении информации, связанной с общими типами во время выполнения.

+0

* общие типы заменены на 'java.lang.Object', их тип стирания *. –

+0

@RohitJain Вы правы, это не всегда 'java.lang.Object' - спасибо! – dasblinkenlight

+1

Именно поэтому он называется «стирание типов». – Mauren

1

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

4

Некоторые обобщения остаются в скомпилированном классе, в частности, включая сигнатуры методов и определения классов. Во время выполнения нет объектов сохраняют свой полный родовой тип, но даже во время выполнения вы можете найти общее определение класса или метода.

Например, если у вас есть

class Foo { 
    List<String> getList() { ... } 

    public static void main(String[] args) { 
    System.out.println(Foo.class.getMethod("getList").getGenericReturnType()); 
    // prints "List<String>" 
    List<String> list = new Foo().getList(); 
    // there is no way to get the "String" parameter on list 
} 
Смежные вопросы