В отличие от .NET Java, дженерики реализованы методом, называемым «стирание типа».
Это означает, что компилятор будет использовать информацию о типе при генерации файлов классов, но не будет передавать эту информацию в байт-код. Если вы посмотрите на скомпилированные классы с помощью javap или подобных инструментов, вы обнаружите, что List<String>
- это простой List
(из Object
) в файле классов, как это было в коде до Java-5.
Код, обращающийся к универсальному списку, будет «переписан» компилятором для включения приведений, которые вы должны были бы написать самостоятельно в более ранних версиях. По сути, следующие два фрагмента кода идентичны с точки зрения байтового кода после того, как с ними закончил компилятор:
Java 5:
List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);
Java 1.4 и ранее:
List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);
При чтении значений из универсального класса в Java 5 автоматически вставляется необходимое приведение к объявленному параметру типа. При вставке компилятор проверит значение, которое вы пытаетесь ввести, и прервет его с ошибкой, если это не строка.
Все было сделано для обеспечения взаимодействия старых библиотек и нового обобщенного кода без необходимости перекомпиляции существующих библиотек. Это главное преимущество по сравнению с .NET, когда универсальные и неуниверсальные классы живут бок о бок, но не могут свободно обмениваться.
У обоих подходов есть свои плюсы и минусы, но так обстоит дело в Java.
Возвращаясь к исходному вопросу: вы не сможете получить информацию о типе во время выполнения, потому что ее просто больше нет, как только компилятор выполнит свою работу. Это, безусловно, ограничивает в некоторых отношениях, и есть некоторые причудливые способы, которые обычно основаны на хранении экземпляра класса где-то, но это не стандартная функция.