Есть языки, которые применяют типы во время компиляции, но забывают о типах во время выполнения. Это называется стиранием типа.
Типы стирания во время выполнения
Например, в C компилятор обеспечивает полную проверку типов. Таким образом, сгенерированный байт-код не будет беспокоиться об информации о типе во время выполнения.
Как две стороны медали с другой стороны. Есть языки, которые выполняют проверку типов во время выполнения (возможно, также во время компиляции). Это называется овеществлением.
Проверяет типы во время выполнения
Например, в Java, даже если вы можете перехитрить компилятор и назначить его компилятору. Типы проверяются во время выполнения.
Классический пример овеществления типа Java,
class TypeReificationSample { public static void main(String[] args) { Object[] strArr = new String[1]; strArr[0] = (Object) 13; } }
Здесь, когда вы компилируете, вы перехитрили компилятор, присвоив ему тип Object. Но когда вы запустите скомпилированный класс, вы увидите ❌
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer at TypeReificationSample.main(TypeReificationSample.java:4)
Это показывает, что Java проверяет массив во время выполнения.
Когда Generics был реализован в Java, было введено стирание типов, чтобы сделать язык обратно совместимым.
Давайте снова возьмем классический пример,
class TypeCheck { public static void main(String[] args) { List<String> strList = new ArrayList<String>(); strList.add("Hello"); String hello = strList.get(0); System.out.println(hello); // "Hello" } }
Проверка общего типа
class GenericTypeCheck { public static void main(String[] args) { List strList = new ArrayList(); strList.add("Hello"); String hello = (String) strList.get(0); System.out.println(hello); // "Hello" } }
Оба класса компилируются в один и тот же байт-код.
class TypeCheck { TypeCheck(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: ldc #4 // String Hello 11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 16: pop 17: aload_1 18: iconst_0 19: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 24: checkcast #7 // class java/lang/String 27: astore_2 28: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 31: aload_2 32: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 35: return }
Проверьте 4-й индекс стека. У него нет информации о типе.
В Java 5 появились переменные аргументы. Это означает, что вы можете передавать несколько аргументов через ...
public void someMethodTakesMultipleArguments(String... args) { } // Pass in generic arguments public <T> void someMethodTakesMultipleArguments(T... generic) { }
...
сообщает компилятору, что он ожидает массив String в первом случае и массив T во втором случае.
Общие аргументы приведут к потенциально небезопасным операциям. Возможно, что метод приведет к приведению или потенциально изменит тип. Это приведет к неуверенности.
Таким образом, компилятор выдаст предупреждение при компиляции.
Note: GenericArguments.java uses unchecked or unsafe operations.
Потому что varargs может вызвать загрязнение кучи.
Varargs method could cause heap pollution from non-reifiable varargs parameter
Чтобы решить эту проблему, Java7 представила аннотацию @SafeVarargs
. Это сообщит компилятору, что метод или конструктор не выполняет потенциально небезопасные операции с параметром varargs
.
Применение аннотации @SafeVarargs
к методу подавит предупреждение компилятора о непроверенных предупреждениях.
Но добавление @SafeVarargs
к потенциально опасному методу приведет к ClassCastException
во время выполнения.
Когда применять эту аннотацию: когда вы передаете переменные аргументы методу или конструктору и не изменяете или не приводите тип объекта. Метод потенциально безопасен.
Где применить эту аннотацию: к методам final
и static
. Это предотвращает переопределение методов. У методов в интерфейсе не должно быть этой аннотации.
If the varargs parameter array is used only to transmit a variable number of arguments from the caller to the method—which is, after all, the purpose of varargs—then the method is safe. - Effective Java