java - Java 泛型中的桥接方法。这个例子正确吗?

标签 java generics bounds type-erasure java-bridge-method

假设我有这个通用类:

class Item<T> {
  private T item;
  public void set(T item) {
    this.item = item;
  }
  public T get() {
    return item;
  } 
}

如果我创建 2 个这样的实例:

Item<Integer> intItem = new Item<Integer>();
Item<String> stringItem = new Item<String>();

这两个实例共享相同的原始类:

  class Item {
    private Object item;
    public void set(Object item) {
      this.item = item;
    }
    public Object get() {
      return item;
    } 
  }

现在,如果我像这样扩展 Item 类:

class IntItem extends Item<Integer>{
  private Integer item;
  public void set(Integer item) {
    this.item = item;
  }
  public Integer get() {
   return item;
  } 
}

创建这些桥接方法:

  class IntItem extends Item<Integer>{
      private Integer item;
      //Bridge method 1
      public void set(Object item) {
        this.item = (Integer) item;
      }
      public void set(Integer item) {
        this.item = item;
      }
      //Bridge method 2
      public Object get() {
       return item;
      }
      public Integer get() {
       return item;
      } 
    }

到目前为止我都做对了吗? 我的问题是,为什么以及何时需要桥接方法? 你能用这个 Item 类做一些例子吗?

我已经阅读了其他答案,但如果没有具体的例子,我仍然无法完全理解。

最佳答案

你几乎猜对了。几乎,因为桥接方法桥接方法调用并且不重复方法实现。您的IntItem类看起来像以下脱糖版本(您可以使用例如 javap 来验证这一点):

class IntItem extends Item<Integer> {

  private Integer item;

  // Bridge method 1
  public void set(Object item) {
    set((Integer) item);
  }

  public void set(Integer item) {
    this.item = item;
  }

  //Bridge method 2
  public Object get() {
   return <Integer>get(); // pseudosyntax
  }

  public Integer get() {
   return item;
  } 
}

在 Java 字节代码中,允许定义两个仅在返回类型上有所不同的方法。这就是为什么可以有两种方法 get您无法使用 Java 语言显式定义它。事实上,您需要以字节码格式命名任何方法调用的参数类型和返回类型。

这就是为什么您首先需要桥接方法的原因。 Java 编译器对泛型类型应用类型删除。这意味着,JVM 不会考虑泛型类型,因为 JVM 会看到 Item<Integer> 的所有出现。作为原始Item 。但是,这不适用于类型的显式命名。最后ItemInt它本身不再是通用的,因为它覆盖了具有显式类型版本的所有方法,这些方法对于具有这些显式类型的 JVM 是可见的。因此,IntItem在其加糖版本中甚至不会覆盖 Item 的任何方法因为签名不兼容。为了使泛型类型对 JVM 透明,Java 编译器需要插入这些实际上覆盖原始实现的桥接方法,以便桥接对 IntItem 中定义的方法的调用。 。这样,您就可以间接重写这些方法并获得您期望的行为。考虑以下场景:

IntItem nonGeneric = new IntItem();
nonGeneric.set(42);

如上所述,IntItem::set(Integer)方法不是泛型的,因为它被非泛型类型的方法覆盖。因此,调用此方法不涉及类型删除。 Java编译器只是编译上面的方法调用来调用带有字节码签名set(Integer): void的方法。 。正如你所期望的那样。

但是,当查看以下代码时:

Item<Integer> generic = ...;
generic.set(42);

编译器无法确定 generic变量包含 IntItem 的实例或 Item (或任何其他兼容的类)。因此,不能保证具有字节码签名 set(Integer): void 的方法甚至存在。这就是 Java 编译器应用类型删除并查看 Item<Integer> 的原因。就好像它是原始类型一样。通过查看原始类型,方法set(Object): void调用 Item 上定义的它本身,因此总是存在。

因此,IntItem类无法确定其方法是使用已删除类型(继承自 Item )的方法还是使用显式类型的方法来调用。因此,桥接方法被隐式实现以创建单一版本的事实。通过动态调度桥,您可以覆盖 set(Integer)IntItem 的子类中并且桥接方法仍然有效。

桥接方法还可用于实现方法的协变返回类型。该功能是在引入泛型时添加的,因为桥接方法已经作为概念存在。可以说,这个功能是免费的。桥接方法还有第三种应用,即 implement a visibility bridge 。这对于克服反射引擎的访问限制是必要的。

关于java - Java 泛型中的桥接方法。这个例子正确吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24625612/

相关文章:

Java 字符串分割

Java 泛型 - 类型删除后的方法

Android绑定(bind)服务和AIDL服务

iphone - 如何从我的 View Controller 为多个 subview 设置 CAlayer 边界

java - 如果网络可用,则从服务器获取 JSON

java - 用于文件上传的 Servlet 3.0 流式 api

java - Apache Tika - MIME(内容类型)检测不正确

generics - 模式匹配,其中模式基于(函数)参数

c# - 如何将两个 C# 列表拼接成一个?或者可能使用不同的集合类型?

c - 使用指向 one-past-malloc 的指针是否定义明确?