【问题标题】:How bridge method works?桥接法是如何工作的?
【发布时间】:2014-01-25 08:51:04
【问题描述】:

我是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;

    }
}

当我运行此代码时,出现以下错误

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 的另一个最重要的问题是当我们扩展一个类时,当我们调用超级构造函数时,虽然没有创建超级对象,那么调用超级有什么用。请解释一下。

【问题讨论】:

    标签: java oop generics


    【解决方案1】:

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

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

    当我们调用超级构造函数时,虽然没有创建超级对象,那么调用超级有什么用

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

    【讨论】:

      【解决方案2】:

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

      是的,没错。这就是 bridge 方法 发挥作用的时候。发生的情况是,使用该类型擦除,子类中的 setData() 方法不再是超类 setData() 方法的覆盖等效项。因此,编译时的行为不会持续到运行时。为了保留这种行为,编译器会在内部生成一个桥接方法

      Bridge method is often generated by the compiler, when a type extends or implements a parameterized class or interface and type erasure changes the signature of any inherited method.

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

      // 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 类构造函数。

      进一步阅读:

      【讨论】:

      • 那么如果您说要添加桥接方法,为什么第一个示例会失败,对不起,如果我完全理解您的回答
      • @sumedha 是的,添加了桥接方法。这并不会真正导致您在main 中的方法调用失败。那完全是内部的。问题是,您将 String 类型参数传递给方法(这正是一个桥接方法),然后在内部尝试将该参数转换为 Integer 类型,这就是失败的地方。
      • 如果第二个示例具有桥接方法的副本,那么为什么在第二个示例中它会失败,请解释
      • @sumedha 你说的是第二个例子吗?如果它是您问题中的那个,那么不,它并不是真正的桥接方法的复制品。那里没有桥接方法。请记住,桥接方法是在子类中创建的
      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多