Что такое стирание типов и как оно функционирует?

Почему возникает ошибка в следующем коде? 

```java
Pair emp = person;
emp.setFirst(new Person());
Person pers = emp.getFirst(); // ОШИБКА!! Object cannot be converted to Person
```

В данном случае, если в первом примере из-за стирания типов возвращается объект типа Object, который автоматически преобразуется в Person, то почему это преобразование не работает во втором случае? Замечу, что явное приведение работает следующим образом: 

```java
Person pers = (Person)emp.getFirst();
``` 

Объясните, пожалуйста, различия в поведении между этими двумя примерами.
  • 19 ноября 2024 г. 20:03
Ответы на вопрос 2
Стирание типов (type erasure) в Java — это механизм, который используется для обеспечения обратной совместимости с версиями Java до появления дженериков (generics). Оно позволяет компилятору удалять информацию о дженериках (например, параметризованные типы) во время компиляции, сохраняя только информацию о не параметризованных версиях классов. В результате, все дженерики становятся "сырыми" типами (raw types), что соответствует типу `Object`.

### Ошибка в приведённом вами коде

Рассмотрим ваш код:

```java
Pair emp = person;
emp.setFirst(new Person());
Person pers = emp.getFirst(); // ОШИБКА!! Object cannot be converted to Person
```

Здесь `Pair` — это обобщённый класс, который, предположительно, должен работать с дженериками. Однако из-за стирания типов, когда вы используете `Pair` как необобщенный тип (тип `Pair` без параметров), метод `getFirst()` возвращает значение типа `Object`, а не `Person`. Из-за этого прямое присвоение `Person pers = emp.getFirst();` невозможно, так как `Object` не может быть автоматически преобразован в `Person` без явного приведения типа.

В вашем втором примере:

```java
Person pers = (Person)emp.getFirst();
```

Вы явно указываете компилятору, что вы ожидаете объект типа `Person`, и поэтому приводите `Object` к `Person` с помощью явного приведения.

### Почему это работает?

1. **Стирание типов**: Когда вы создаёте объект типа `Pair`, компилятор не знает, что `Pair` должен содержать значение типа `Person`. Он использует концепцию стирания типов, так что без параметризованного типа он рассматривает его как `Pair<Object>`.

2. **Автоматическое приведение**: В случае автоматического присвоения у вас нет информации о том, что `getFirst()` действительно вернёт `Person`. Компилятор видит это как `Object` и не может выполнить автоматическое приведение без явного указания, что вы ожидаете именно `Person`.

3. **Явное приведение**: Когда вы используете явное приведение, вы берёте на себя ответственность за то, что объект, к которому вы обращаетесь, действительно является `Person`. Это приводит к потенциальной ошибке во время выполнения, если данный объект не является экземпляром `Person`.

### Рекомендации

Чтобы избежать подобных ошибок, рекомендуется всегда использовать дженерики при работе с типизированными классами. Например, так:

```java
Pair<Person> emp = person;
emp.setFirst(new Person());
Person pers = emp.getFirst(); // Теперь это работает корректно
```

Таким образом, с использованием дженериков вы избегаете проблем, связанных со стиранием типов и получите типобезопасное окружение.
Потому что в коде 
Pair emp = person;
…не угадываются шаблонные параметры, а просто берётся Pair.
Похожие вопросы