← Назад к вопросам

Что такое стирание типа в Generics?

1.7 Middle🔥 171 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Стирание типа в Generics

Стирание типа (Type Erasure) — это процесс, при котором информация о типе параметра в обобщённых классах и методах удаляется во время компиляции. Java преобразует код с generics в обычный код со стиранием типов параметров, что позволило добавить поддержку generics в Java 5 без нарушения обратной совместимости.

Как работает стирание типа

Вовремя компиляции Java преобразует:

List<String> list = new ArrayList<>();
list.add("hello");
String value = list.get(0);

В следующий код:

List list = new ArrayList();
list.add("hello");
String value = (String) list.get(0);

Как видишь, информация о типе String полностью удаляется, и вместо этого добавляется явное приведение типа.

Пример с методом

До стирания типа:

public <T> T getValue(List<T> list) {
    return list.get(0);
}

После стирания типа:

public Object getValue(List list) {
    return list.get(0);
}

Параметр типа T заменяется на Object — верхнюю границу типа (upper bound).

Ограничения с bounded типами

Если у параметра типа есть ограничение (bound):

public <T extends Number> double sum(List<T> numbers) {
    double result = 0;
    for (T num : numbers) {
        result += num.doubleValue();
    }
    return result;
}

После стирания типа T заменяется на его bound Number:

public double sum(List numbers) {
    double result = 0;
    for (Object num : numbers) {
        result += ((Number) num).doubleValue();
    }
    return result;
}

Проблемы, возникающие из-за стирания типов

1. Невозможно использовать instanceof с параметрами типа:

// ОШИБКА: не скомпилируется
public <T> boolean isInstanceOf(Object obj) {
    return obj instanceof T; // Ошибка компиляции
}

// Правильно: используй вместо этого
public <T> boolean isInstanceOf(Object obj, Class<T> type) {
    return type.isInstance(obj);
}

2. Нельзя создавать массивы параметризованных типов:

// ОШИБКА
List<String>[] arrayOfLists = new List<String>[10];

// Правильно
List<String>[] arrayOfLists = new ArrayList[10]; // unchecked warning
// или
List<List<String>> listOfLists = new ArrayList<>();

3. Перегрузка методов:

public class Box {
    // ОШИБКА: не скомпилируется
    public void add(List<String> list) { }
    public void add(List<Integer> list) { }
    
    // После стирания типа оба метода имеют одинаковую сигнатуру
    // public void add(List list) { }
}

4. Нельзя получить информацию о типе параметра во время выполнения:

public <T> void printType(List<T> list) {
    // Это напечатает просто "class java.util.ArrayList"
    System.out.println(list.getClass());
    // Информация о <T> потеряна
}

public <T> Class<T> getParameterType(List<T> list) {
    // Невозможно получить Class<T> из list
    // Нужно передавать его явно:
    return null;
}

Workaround'ы для работы с типами

Использование Class параметра:

public <T> T createInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException {
    return clazz.newInstance();
}

public class Box<T> {
    private Class<T> type;
    
    public Box(Class<T> type) {
        this.type = type;
    }
    
    public boolean isOfType(Object obj) {
        return type.isInstance(obj);
    }
}

Использование рефлексии и TypeToken:

import com.google.common.reflect.TypeToken;

public <T> T fromJson(String json, TypeToken<T> typeToken) {
    // TypeToken сохраняет информацию о типе благодаря анонимному классу
    Type type = typeToken.getType();
    // Используем тип для десериализации
    return null;
}

// Использование:
List<String> list = fromJson(jsonStr, new TypeToken<List<String>>() {});

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

// До компиляции
public class Container<T> {
    private T value;
    
    public T getValue() {
        return value;
    }
    
    public void setValue(T value) {
        this.value = value;
    }
}

// После стирания типов
public class Container {
    private Object value;
    
    public Object getValue() {
        return value;
    }
    
    public void setValue(Object value) {
        this.value = value;
    }
}

Почему Java реализовала стирание типов

Glass использует стирание типов ради обратной совместимости. Это позволило добавить generics в Java 5 без изменения байт-кода и виртуальной машины. Код, скомпилированный без generics, может работать вместе с кодом, использующим generics.

Ключевые моменты

  • Стирание типов происходит только во время компиляции, в runtime информация о типах параметров недоступна
  • Проверка типов выполняется компилятором, а не в runtime
  • Unchecked warnings появляются, когда компилятор не может гарантировать type safety
  • Это является источником многих ограничений обобщённых типов в Java
  • Другие языки (C#, Kotlin) реализуют generics иначе, без стирания типов
Что такое стирание типа в Generics? | PrepBro