Рубрики
Без рубрики

Стирание типов в Java Объяснено

Узнайте о важном механизме в том, как Java обрабатывает удаление общего типа.

Автор оригинала: baeldung.

1. Обзор

В этой краткой статье мы обсудим основы важного механизма в универсальных приложениях Java, известного как стирание типов.

2. Что Такое Стирание Типа?

Удаление типов можно объяснить как процесс применения ограничений типа только во время компиляции и удаления информации о типе элемента во время выполнения.

Например:

public static   boolean containsElement(E [] elements, E element){
    for (E e : elements){
        if(e.equals(element)){
            return true;
        }
    }
    return false;
}

Компилятор заменяет несвязанный тип E фактическим типом Объекта :

public static  boolean containsElement(Object [] elements, Object element){
    for (Object e : elements){
        if(e.equals(element)){
            return true;
        }
    }
    return false;
}

Поэтому компилятор обеспечивает типобезопасность нашего кода и предотвращает ошибки во время выполнения.

3. Типы стирания типов

Стирание типов может происходить на уровне классов (или переменных) и методов.

3.1. Стирание типа Класса

На уровне класса компилятор отбрасывает параметры типа в классе и заменяет их своей первой привязкой или Объектом , если параметр типа не привязан.

Давайте реализуем Стек с использованием массива:

public class Stack {
    private E[] stackContent;

    public Stack(int capacity) {
        this.stackContent = (E[]) new Object[capacity];
    }

    public void push(E data) {
        // ..
    }

    public E pop() {
        // ..
    }
}

При компиляции компилятор заменяет параметр несвязанного типа E на Объект :

public class Stack {
    private Object[] stackContent;

    public Stack(int capacity) {
        this.stackContent = (Object[]) new Object[capacity];
    }

    public void push(Object data) {
        // ..
    }

    public Object pop() {
        // ..
    }
}

В случае, когда параметр типа E привязан:

public class BoundStack> {
    private E[] stackContent;

    public BoundStack(int capacity) {
        this.stackContent = (E[]) new Object[capacity];
    }

    public void push(E data) {
        // ..
    }

    public E pop() {
        // ..
    }
}

Компилятор заменит параметр связанного типа E первым связанным классом, Сопоставимым в этом случае :

public class BoundStack {
    private Comparable [] stackContent;

    public BoundStack(int capacity) {
        this.stackContent = (Comparable[]) new Object[capacity];
    }

    public void push(Comparable data) {
        // ..
    }

    public Comparable pop() {
        // ..
    }
}

3.2. Стирание Типа Метода

Для удаления типов на уровне метода параметр типа метода не сохраняется, а скорее преобразуется в его родительский тип Объект , если он несвязан или является первым связанным классом, когда он связан.

Давайте рассмотрим метод отображения содержимого любого заданного массива:

public static  void printArray(E[] array) {
    for (E element : array) {
        System.out.printf("%s ", element);
    }
}

При компиляции компилятор заменяет параметр типа E на Объект :

public static void printArray(Object[] array) {
    for (Object element : array) {
        System.out.printf("%s ", element);
    }
}

Для параметра типа связанного метода:

public static > void printArray(E[] array) {
    for (E element : array) {
        System.out.printf("%s ", element);
    }
}

У нас будет параметр типа E удален и заменен на Сопоставимый:

public static void printArray(Comparable[] array) {
    for (Comparable element : array) {
        System.out.printf("%s ", element);
    }
}

4. Крайние Случаи

Иногда в процессе стирания типов компилятор создает синтетический метод для различения похожих методов. Они могут быть получены из сигнатур методов, расширяющих один и тот же первый связанный класс.

Давайте создадим новый класс, который расширяет нашу предыдущую реализацию Стека. Пожалуйста , обратите внимание, что это относится к классу Stack , который мы создали в разделе 3.1 , а не java.util.Стек .

public class IntegerStack extends Stack {

    public IntegerStack(int capacity) {
        super(capacity);
    }

    public void push(Integer value) {
        super.push(value);
    }
}

Теперь давайте рассмотрим следующий код:

IntegerStack integerStack = new IntegerStack(5);
Stack stack = integerStack;
stack.push("Hello");
Integer data = integerStack.pop();

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

IntegerStack integerStack = new IntegerStack(5);
Stack stack = (IntegerStack) integerStack;
stack.push("Hello");
Integer data = (String) integerStack.pop();

Обратите внимание, как мы можем нажать S tring на Целочисленный стек – потому что Целочисленный стек унаследован push(объект) из родительского класса Стек . Это, конечно, неверно – так как это должно быть целое число, так как integerStack является Стеком<Целое число> типа.

Поэтому неудивительно, что попытка вставить строку и присвоить Целое число вызывает исключение ClassCastException из приведения, вставленного во время push компилятором.

4.1. Мостовые методы

Чтобы решить описанный выше пограничный случай, компилятор иногда создает метод моста. Это синтетический метод, созданный компилятором Java при компиляции класса или интерфейса, который расширяет параметризованный класс или реализует параметризованный интерфейс, где сигнатуры методов могут немного отличаться или быть неоднозначными.

В приведенном выше примере компилятор Java сохраняет полиморфизм универсальных типов после удаления, гарантируя отсутствие несоответствия сигнатур метода между Целым стеком s push(Целое число) методом и Стеком s push(объект) методом.

Следовательно, компилятор создает здесь метод моста:

public class IntegerStack extends Stack {
    // Bridge method generated by the compiler
    
    public void push(Object value) {
        push((Integer)value);
    }

    public void push(Integer value) {
        super.push(value);
    }
}

Следовательно, Стек метод класса push после удаления типа делегирует исходный push метод Целочисленного стека класса.

5. Заключение

В этом учебном пособии мы обсудили концепцию стирания типов с примерами в переменных и методах параметров типов.

Вы можете прочитать больше об этих концепциях:

Как всегда, исходный код, сопровождающий эту статью, доступен на GitHub .