Есть языки, которые применяют типы во время компиляции, но забывают о типах во время выполнения. Это называется стиранием типа.

Типы стирания во время выполнения

Например, в 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