Здесь следует учитывать два разных контекста: время компиляции и время выполнения.
Разница между классами generi c и non-generi c во время компиляции заключается в наличии одного или более общих c аргументов в сигнатуре класса или метода.
Во время компиляции Test<String>
и Test<Integer>
обрабатываются как разные типы. Преимущество этого заключается в том, что если вы попытаетесь использовать Test<String>
где-нибудь, где ожидается Test<Integer>
, компилятор пожалуется.
sObj = sObj2; // compiler complains
Это хорошо, потому что, вероятно, это была простая ошибка, который вы хотите поймать и исправить прямо сейчас.
Во время выполнения каждый объект имеет небольшие дополнительные метаданные, связанные с ним, чтобы сообщить вам, что это за тип. Это полезно по многим причинам, включая Reflection (вызов таких методов, как .getClass()
) и предотвращение недопустимых приведений:
Object obj = "foo";
Integer i = (Integer) obj; // compiler doesn't complain, but an exception is thrown
Type Erasure означает, что этот дополнительный небольшой бит метаданных содержит нет информации об общих c типах во время выполнения.
if(sObj.getClass().equals(sObj2.getClass())) { // This will be true
sObj = (Test<String>) sObj1; // this will not produce an error.
}
У этого есть некоторые недостатки. Например, в приведенном выше коде явно есть ошибка в logi c, но вместо того, чтобы увидеть недопустимое приведение в строке выше, вы, вероятно, получите недопустимое приведение в совершенно другой части кода, когда вы попробуйте преобразовать Integer
в String
.
Но к тому времени, когда язык Java добавил дженерики, реализация его любым другим способом имела бы множество других недостатков с точки зрения усилий и потеря обратной совместимости. Поэтому они решили использовать Type Erasure вместо Reified Generics.
См. Также: Почему меня должно волновать, что Java не имеет reified generics?