Тип безопасности с дженериками в Java
Я столкнулся с поведением дженериков в Java, которые я полностью не могу понять (с моим фоном .NET).
public class TestGeneric<T>
{
public void get (Object arg)
{
T temp = (T) arg;
System.out.println(temp.toString());
return;
}
}
TestGeneric<Integer> tg = new TestGeneric<Integer>();
tg.get("Crack!!!");
Скажите, пожалуйста, почему я не получаю ClassCastException в том, что в Idea я вижу temp как String
после присваивания и имеющий значение "Crack!!!"
. Кроме того, как я могу удалить это ClassCastException? Я использую JDK 1.7.0_07 для Windows 7 x64.
Ответы
Ответ 1
Причина, по которой вы не получаете исключение класса, заключается в том, что Java-дженерики реализуются посредством стирания типа. В отличие от общих дженериков .NET, которые требовали значительных изменений в CLS, генерические файлы Java обрабатываются полностью во время компиляции. Во время выполнения приведение к T
игнорируется. Чтобы проверить тип во время выполнения, вам нужно сохранить Class<T>
и использовать его методы для проверки типа передаваемого параметра:
public class TestGeneric<T>
{
private Class<T> genClass;
public TestGeneric(Class<T> t) {genClass = t;}
public void get (Object arg)
{
if (!genClass.isInstance(arg)) {
throw new ClassCastException();
}
T temp = (T) arg;
System.out.println(temp.toString());
return;
}
}
TestGeneric<Integer> tg = new TestGeneric<Integer>(Integer.class);
tg.get("Bang!!!"); // Now that a real Bang!!!
Ответ 2
Это связано с тем, что общий тип T не имеет определенных границ, поэтому он рассматривается как объект. В этом случае приведение чего-то к T не вызовет ClassCastException
.
Однако, если ваше определение класса было public class TestGeneric<T extends Number>
, тогда вы получите ClassCastException, если вы передали в String в get().
Ответ 3
Поучительное упражнение подумать о том, что не общий код, который генерируется общим кодом, выглядит следующим образом:
public class TestGeneric
{
public void get (Object arg)
{
Object temp = (Object) arg;
System.out.println(temp.toString());
return;
}
}
TestGeneric tg = new TestGeneric();
tg.get("Crack!!!"); // should there be any problem?
Ответ 4
Это связано с type-erasure
. Это означает, что в Java все дженерики сводятся к Object
во время выполнения. Таким образом, вы отбросили ваш String
до Object
, который полностью прекрасен. А так как toString
реализован на Object
, опять же исключение.
Вот ссылка на тип-стирание
Единственный способ получить ClassCastException
- передать экземпляр Class<T>
в общий тип, а затем сделать myClass.isInstance(arg)
и выбросить исключение, если false
Ответ 5
Всегда помните, что Generics в Java - это объекты времени компиляции. Во время выполнения он не имеет ничего общего. Позвольте мне продемонстрировать вам ваш код oqn.
public class TestGeneric<T>
{
public void get (Object arg)
{
T temp = (T) arg;
System.out.println(temp.toString());
System.out.println(temp.getClass());
return;
}
public static void main(String args[])
{
TestGeneric<Integer> tg = new TestGeneric<Integer>();
tg.get("Crack!!!");
}
}
а выход -
Crack!!!
class java.lang.String
Теперь имеет смысл? Объект - высший суперкласс. Таким образом, он может получить объект String из-за полиморфизма. Хотя вы производите тип или, скорее, я скажу, что сделать целочисленную опорную точку для строкового объекта, который Java знает внутри, это объект String во время выполнения. Было бы проблемой, если toString() не был определен в классе Integer. Вызываемая функция должна быть определена в ссылке, но реализация будет подобрана соответствующим образом в время выполнения из объекта .
Ответ 6
Если вы это сделаете, вам даже не нужно проверять ClassCastException, и он даже не сможет скомпилировать.
public class TestGeneric<T> {
public void get (T arg){
System.out.println(arg.toString());
}
}
TestGeneric<Integer> tg = new TestGeneric<Integer>();
tg.get("Crack!!!");
Ответ 7
Тип Integer
имеет метод toString
. Фактически каждый Object
имеет этот метод, поэтому ClassCastException
не будет встречаться.
Вы не назовете какой-либо String
-специфический метод для вашего объекта, таким образом не произошло никакого исключения.
Причиной этого является то, что во время выполнения вы не увидите параметры типа type erasure.
Дело в том, что после компиляции кода вы больше не сможете видеть параметры типового типа, потому что они стираются.
Здесь возникает еще один вопрос, объясняющий исключение класса: объяснение
В этом коде вы можете видеть, что пользователь попытался явно указать на String
не общий параметр.
Таким образом, вы можете назвать это недостатком java по сравнению с С#.