Java Type Erasure: правила вставки приведения?

В руководстве по стиранию типов Java, по-видимому, не подробно описывается определенные правила вставки приведения компилятором. Может кто-нибудь объяснить конкретные правила, которые вызывают преобразование, подробно описанное в учебнике (воспроизведено ниже):

public class Node<T> {
    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.

В частности, мне интересно, какие правила вызывают вставку (MyNode) и (String). Когда накладывается гипс и как выбирается тип гипса?


person Kvass    schedule 12.04.2015    source источник
comment
Компилятор не вставляет приведения. Вы, как программист, должны вставлять приведения типов, если это необходимо, и компилятор проверит, верны они или нет.   -  person Oleg Estekhin    schedule 12.04.2015
comment
Это неправда, это добавит актеров за кадром.   -  person Crazyjavahacking    schedule 12.04.2015


Ответы (2)


  1. MyNode mn = new MyNode(5);

    • will create an instance of MyNode which defines the generic type T of interface Node as Integer
    • приведение: приведение не требуется разработчиком, приведение не добавляется компилятором
  2. Node n = (MyNode)mn;

    • this will basically tell the compiler forget about the generic type T and use the interface Node completely without generics which will have the following consequence: imagine generic type T to be treated as java.lang.Object
    • приведение: приведение не требуется разработчиком, приведение не добавляется компилятором
  3. n.setData("Hello");

    • will allow you to add any kind ob object because T is treated as Object (String, Integer, array, anything else)
    • приведение: приведение не требуется разработчиком, приведение не добавляется компилятором
  4. Integer x = mn.data;

    • nm.data should return an Integer type as Integer is defined as generic type argument T in the MyNode class
    • однако, поскольку вы использовали необработанные типы, которые позволили вам вместо этого добавить String, nm.data содержит экземпляр String
    • приведение: разработчику не требуется приведение, однако компилятор добавит приведения к Integer за кулисами для вас, и из-за несоответствия типов вы получите ClassCastException
person Crazyjavahacking    schedule 12.04.2015
comment
Не могли бы вы уточнить, когда вставляются приведения? Я думаю, что понимаю аспект «почему», но мне не совсем понятен набор обстоятельств, в которых уместны приведения. - person Kvass; 12.04.2015
comment
Добавлены объяснения кастинга. - person Crazyjavahacking; 12.04.2015
comment
Можете ли вы объяснить, почему вы говорите, что компилятор добавит приведение Integer, если мы увидим, что в коде он добавляет приведение для String? Также кажется, что mn приводится к MyNode, почему? - person Eitanos30; 22.10.2019

ClassCastException будет throw при вызове

n.setData("Hello");

Это связано с тем, что компилятор создает методы моста для сохранения полиморфизма. Метод моста будет выглядеть так:

 public void setData(Object data) {
       setData((Integer)data);   //the exception is thrown here
  }

поскольку экземпляр строки не может быть преобразован в Integer, будет выбрано ClassCastException.

Вы можете прочитать о методах моста здесь.

person Bala    schedule 01.06.2015
comment
+1. Действительно, я сам протестировал код, и ClassCastException должен быть выбран в этой строке, а не в следующей. Учебники по Java - эффекты стирания типов и методы моста ошибочны. Более того, если вы попытаетесь декомпилировать файл .class, вместо этого вы должны получить Integer x = (Integer)mn.data;. Я использую JDK 8, возможно, эта конкретная страница руководств устарела, хотя весь раздел руководств, посвященный дженерикам, написан для JDK 8. - person blackr1234; 21.03.2018
comment
Добавленный метод моста можно наблюдать с помощью инструмента javap. Выполните javap -c -verbose MyNode.class, и вы увидите 2 метода с именем setData, один из которых является методом моста. Как вы сказали, информация о кастинге в учебнике неверна. Что касается приведения, компилятор добавляет только приведение к Integer для строки Integer x = mn.data. Узел n = mn; не расширяется приведением, потому что на самом деле оно не нуждается в приведении. - person CEGRD; 14.07.2020