Java教程
  • Introduction
  • Getting Started
    • The Java Technology Phenomenon
      • About the Java Technology
      • What Can Java Technology Do?
      • How Will Java Technology Change My Life?
    • The Hello World Application
    • A Closer Look at the Hello World Application
  • Learning the Java Language
    • Object-Oriented Programming Concepts
      • What Is an Object?
      • What Is a Class?
      • What Is Inheritance?
      • What Is an Interface?
      • What Is a package?
    • Language Basics
      • Java Language Keywords
    • Annotations
      • Annotations Basics
      • Declaring an Annotation Type
      • Predefined Annotation Types
      • Repeating Annotations
      • Type Annotations and Pluggable Type Systems
    • Generics
      • Why Use Generics?
      • Generic Types
        • Raw Types
      • Generic Methods
      • Bounded Type Parameters
        • Generic Methods and Bounded Type Parameters
      • Generics, Inheritance, and Subtypes
      • Type Inference
      • Wildcards
        • Upper Bounded Wildcards
        • Unbounded Wildcards
        • Lower Bounded Wildcards
        • Wildcards and Subtyping
        • Wildcard Capture and Helper Methods
        • Guidelines for Wildcard Use
      • Type Erasure
        • Erasure of Generic Types
        • Erasure of Generic Methods
        • Effects of Type Erasure and Bridge Methods
        • Non-Reifiable Types
      • Restrictions on Generics
Powered by GitBook
On this page

Was this helpful?

  1. Learning the Java Language
  2. Generics
  3. Type Erasure

Effects of Type Erasure and Bridge Methods

PreviousErasure of Generic MethodsNextNon-Reifiable Types

Last updated 5 years ago

Was this helpful?

有时类型擦除可能会导致我们无法预料的情况。下面的例子将介绍是如何产生的。例子中展示了编译器有时会创建一个称为Bridge method(桥接函数)的合成方法,这是类型擦除过程的一部分。

下面代码中有两个类:

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 = mn;            // A raw type - compiler throws an unchecked warning
n.setData("Hello");     // Causes a ClassCastException to be thrown.
Integer x = mn.data;

特殊说明:在Orcale的文档中报错的注释写在了最后一行Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.。然而根据实际测试情况n.setData("Hello");就已经报错,运行JDK版本为jdk1.8.0_202

在经过类型擦除以后,上面的代码变成:

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

当代码运行时发现了下面这些事:

  • n.setData("Hello");因为实际执行的是MyNode的实例中的setData(Object)函数。(MyNode类从Node继承了setData(Object))

  • setData(Object)函数体中,n所引用对象中的字段data被赋值为一个String

  • mn所引用的相关对象中的字段data,应该是能被访问的Integer(因为mn是MyNode,也就是Node <Integer>)。

  • 尝试将一个String赋值给一个Integer会导致Java编译器在分配时插入强制转换,从而导致ClassCastException。

Bridge Methods

当编译一个扩展参数化类或者实现参数化接口的类或者接口时,作为类型擦除过程的一部分,编译器可能需要创建一个被称为bridge method(桥接函数)的合成函数。

通常我们不需要担心桥接函数,但是如果其中一个出现在堆栈跟踪中,可能会让人感到困惑。

在类型擦除以后,Node和MyNode类型变成:

public class Node {

    public 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("MyNode.setData");
        super.setData(data);
    }
}

经过类型擦除,函数签名不在匹配。Node中的函数变为setData(Object)而MyNode中的函数变为setData(Integer)。也就是说函数MyNode.setData比没有重写函数Node.setData。

为了解决该问题并确保类型擦除以后泛型类型的[多态],Java编译器生成一个桥接函数来确保子类型能像预期的一样工作。以MyNode为例,编译器为setData生成如下的桥接函数:

class MyNode extends Node {

    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

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

如上所见,在类型擦除以后桥接函数和Node类中的setData函数有着相同的函数签名,最总委托给原始的setData函数。

Bridge Methods