Ответ 1
Он не ClassCastException
потому что вся информация об общем типе ClassCastException
из скомпилированного кода (процесс, называемый стиранием типа). По сути, любой тип параметра заменяется на Object
. Вот почему первая версия работает. Это также, почему код компилируется вообще. Если вы попросите компилятор предупредить о непроверенных или небезопасных операциях с -Xlint:unchecked
, вы получите предупреждение о непроверенном приведении в операторе return
abc()
.
С этим утверждением:
String sss = (Test.<Integer>abc(2)).toString();
история немного другая. В то время как параметр типа T
заменяется на Object
, вызывающий код преобразуется в байтовый код, который явно приводит результат к Integer
. Это как если бы код был написан методом с подписью static Object abc(Object)
а оператор был написан:
String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();
То есть, не только преобразование внутри abc()
исчезает из-за стирания типа, но и новое приведение вставляется компилятором в вызывающий код. Это приведение генерирует ClassCastException
во время выполнения, потому что объект, возвращаемый из abc()
является String
, а не Integer
.
Обратите внимание, что утверждение
String ss = "" + (Test.<Integer>abc(2));
не требует приведения, потому что компилятор просто передает объект, возвращенный abc()
в операцию конкатенации строк для объектов. (Детали того, как это делается, зависят от компилятора Java, но это либо вызов метода добавления StringBuilder
либо, StringBuilder
с Java 9, вызов метода, созданного StringConcatFactory
.) Детали здесь не имеют значения; Дело в том, что компилятор достаточно умен, чтобы признать, что приведение не требуется.