桥接方法如何工作?

sum*_*dha 11 java oop generics

我是java的新手.我仍然觉得我必须了解很多,所以如果这个问题看起来很愚蠢,请原谅我.现在我正在浏览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;

    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行此代码时,我得到以下错误

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)
Run Code Online (Sandbox Code Playgroud)

下面是混淆
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

    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码我没有错误,代码运行正常

从文档两者相同?为什么这种行为不同

现在关于oop的另一个最重要的问题是当我们扩展一个类时,当我们调用超级构造函数时,虽然没有创建超级对象,那么调用super的用途是什么.请解释.

Ted*_*ham 5

从文档两者相同?为什么这种行为不同

两个代码示例之间存在不同行为的原因是因为当您用Object替换泛型T时,间接导致setData()不再覆盖超类中的setData().由于参数类型不同,您只需在第二个示例中重载它.所以在第二个例子中,你直接调用了带有Object的超类.在第一个示例中,您正在调用带有Integer的子类.

当我们调用超级构造函数但是没有创建超级对象时,调用超级的用途是什么

超类和子类是相同的对象(其代码分为两个类).因此,调用超类构造函数来初始化超类中定义的任何字段.在您的示例中,如果您从未调用过super(数据),那么将永远不会设置this.data.


Roh*_*ain 5

如果您通过博客阅读,他们会说类型擦除将替换类型参数将替换上面的代码,如下所示

是的,这是对的.这就是桥接方法付诸行动的时候.发生的情况是,在类型擦除的情况下,setData()子类中的方法不再是超类setData()方法的覆盖等价物.因此,编译时的行为不会持续到运行时.为了保留行为,编译器在内部生成桥接方法.

Bridge方法通常由编译器生成,当类型扩展或实现参数化类或接口时,类型擦除会更改任何继承方法的签名.

这是它的工作原理.编译器在子类中生成以下方法:

// Bridge method
public void setData(Object data) {
    setData((Integer) data);
}
Run Code Online (Sandbox Code Playgroud)

现在,这个方法覆盖了setData(Object)超类的方法.正如您所注意到的,此方法仅在内部调用原始方法,并将data其转换为Integer.这是你得到的地方ClassCastException.该data是真正的String类型,不能被强制转换为Integer.

当我运行上面的代码我没有错误,代码运行正常

如上所述,在类型擦除之后,setData()子类中的方法不会覆盖超类方法中的方法.因此,当您在Node引用时调用方法时,它将Node仅调用类方法,其签名为:

public void setData(Object data) {
    System.out.println("Node.setData");
    this.data = data;
}
Run Code Online (Sandbox Code Playgroud)

这不会有问题,因为你没有投出任何东西.您将String类型存储dataObject类型引用.那样就好.

当我们调用超级构造函数但是没有创建超级对象时,调用超级的用途是什么.

那应该是一个单独的问题.无论如何,关键是,对象的状态包括在该类中声明的数据成员,以及它的所有超类.该特定类中的构造函数只会初始化该类中的状态.要初始化超类中对象的状态,必须链接super类构造函数.

进一步阅读:


Jen*_*der 4

在第二个版本中MyNode.setData不会覆盖,Node.setData因为它具有不同的签名。因此,在第二个示例中,不会发生动态调度并被Node.setData调用,它可以很好地处理字符串。

在第一个示例中,MyNode.setData确实 override Node.setData,因此它被调用,但它无法处理Strings,它只能处理Integers 所以你得到了ClassCastException.

因此,如果该博客声称这正是内部发生的情况,那就是错误的。他们可能的意思是:它的工作原理是这样的,如果在第二个示例中MyNode.setData仍然会覆盖。Node.setData