Как работает мостовой метод?

Я новичок в java.I все еще чувствую, что мне нужно много разбираться, поэтому, если этот вопрос кажется глупым, простите меня. Теперь я прошел http://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html

Здесь я нашел много путаницы.

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);
    }

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

    }
}

Когда я запустил этот код, я оказался ниже ошибки

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at MyNode.setData(MyNode.java:1)
at MyNode.main(MyNode.java:14)

Ниже приведены путаницы 1) Почему отображается строка номер 1
2) Если вы читаете в блоге, то говорят, что стирание типа заменит параметр типа, заменит выше код ниже

public class Node {

    private Object data;

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

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

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

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

    }
}

когда я запускаю вышеуказанный код, я не получаю ошибки, код работает нормально

из документации оба одинаковые? почему это отличается поведением

теперь другой самый важный вопрос в oop - это когда мы расширяем один класс, и когда мы называем супер-конструктор, хотя супер-объект не создается, то в чем смысл вызова super. Пожалуйста, объясните.

Ответ 1

из документации оба одинаковые? почему это отличается поведением

Причина, по которой существует различное поведение между двумя примерами кода, заключается в том, что, когда вы заменили общий T на Object, вы косвенно вызвали setData(), чтобы больше не переопределять setData() в суперклассе. Поскольку типы параметров различны, вы просто перегружаете его во втором примере. Итак, во втором примере вы вызываете суперкласс напрямую, который принимает объект. В первом примере вы вызываете подкласс, который принимает Integer.

когда мы вызываем супер-конструктор, хотя супер-объект не создается, то что такое использование вызова super

Суперкласс и подкласс являются одним и тем же объектом (с его разделом кода между двумя классами). Поэтому конструктор суперкласса вызывается для инициализации любых полей, определенных в суперклассе. В вашем примере, если вы никогда не называли super (data), тогда эта информация никогда не была бы установлена.

Ответ 2

Если вы читаете в блоге, то говорят, что стирание типа заменит параметр типа, заменит выше код ниже

Да, это правильно. И это когда метод моста вступает в действие. Что происходит, при стирании этого типа метод setData() в подклассе уже не является эквивалентным переопределением для метода суперкласса setData(). Таким образом, поведение во время компиляции не сохраняется до времени выполнения. Чтобы сохранить поведение, компилятор внутренне генерирует мостовой метод.

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

И вот как это работает. Компилятор генерирует следующий подкласс в подклассе:

// Bridge method
public void setData(Object data) {
    setData((Integer) data);
}

Теперь этот метод переопределяет метод setData(Object) суперкласса. И, как вы заметили, этот метод внутренне вызывает только оригинальный метод, отбрасывая data до Integer. Здесь вы получите ClassCastException. data действительно имеет тип String, который нельзя отнести к Integer.

когда я запускаю вышеуказанный код, я не получаю ошибки, код работает нормально

Как я уже говорил выше, после стирания типа метод setData() в подклассе не переопределяет метод класса суперкласса. Итак, когда вы вызываете метод на ссылке Node, он будет вызывать только метод класса Node, подпись которого:

public void setData(Object data) {
    System.out.println("Node.setData");
    this.data = data;
}

И не будет проблем с этим, потому что вы ничего не бросаете. Вы сохраняете тип String data для ссылки типа Object. Это нормально.

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

Ну, это должен был быть отдельный вопрос. В любом случае, суть в том, что состояние объекта состоит из членов данных, объявленных в этом классе, и всех его суперклассов. Конструктор в этом конкретном классе будет инициализировать только состояние этого класса. Чтобы инициализировать состояние объекта в суперклассе, вы должны связать конструктор класса super.

Дальнейшее чтение:

Ответ 3

Во второй версии MyNode.setData не переопределяет Node.setData, так как имеет другую подпись. Поэтому во втором примере не происходит динамической диспетчеризации и вызывается Node.setData, который может обрабатывать String просто отлично.

В первом примере MyNode.setData переопределяет Node.setData, поэтому он вызывается, но не может обрабатывать String s, он может обрабатывать только Integer, поэтому вы получаете ClassCastException.

Итак, если блог утверждает, что это именно то, что происходит внутри страны, это неправильно. Что они, вероятно, имеют в виду: он работает так, если MyNode.setData по-прежнему будет переопределять Node.setData во втором примере.