简体   繁体   English

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

[英]Bridge methods in Java generics. Is this example correct?

Let's say I have this generic class: 假设我有这个通用类:

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

If I create 2 instances like this: 如果我创建2个这样的实例:

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

The 2 instances share this same raw class: 这两个实例共享相同的原始类:

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

Now, if I extends the class Item like this: 现在,如果我像这样扩展类Item:

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

These bridge methods are created: 创建了以下桥接方法:

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

Have I got it right until here? 直到这里我都正确吗? My question is, why and when are bridge methods necessary? 我的问题是,为什么以及何时需要桥接方法? Can you make some example using this Item class? 您可以使用此Item类作为示例吗?

I already read other answers, but I still do not get it completely without a concrete example. 我已经阅读了其他答案,但是如果没有具体示例,我仍然无法完全理解。

You almost got it correct. 您几乎正确了。 Almost, because bridge methods bridge methods calls and do not duplicate method implementations. 几乎,因为桥接方法桥接方法调用,并且不重复方法实现。 Your IntItem class would look like the following desugared version (you can verify this using for example javap ): 您的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;
  } 
}

Within Java byte code, it is allowed to define two methods that only differ by their return type. 在Java字节码中,允许定义两个仅在返回类型上有所不同的方法。 This is why there can be two methods get which you could not define explicitly using the Java language. 这就是为什么可以有两种方法get你无法明确定义使用Java语言。 As a matter of fact, you need to name the parameter types and the return type on any method invocation within the byte code format. 实际上,您需要在字节码格式内的任何方法调用中命名参数类型和返回类型。

And this is why you need bridge methods in the first place. 这就是为什么您首先需要桥接方法的原因。 The Java compiler applies a type erasure on generic types. Java编译器对通用类型应用类型擦除。 This means, generic types are not considered by the JVM which sees all occurences of Item<Integer> as a raw Item . 这意味着JVM不会考虑泛型类型,因此会将Item<Integer>所有出现都视为原始Item However, this does not work well with the explicit naming of types. 但是,这不适用于类型的显式命名。 In the end, ItemInt is itself not longer generic as it overrides all methods with explicitly typed versions which would be visible to the JVM with these explicit types. 最后, ItemInt本身不再通用,因为它会覆盖具有显式类型的版本的所有方法,这些方法对于具有这些显式类型的JVM而言是可见的。 Thus, IntItem would in its sugared version not even override any methods of Item because the signatures are not compatible. 因此,由于签名不兼容, IntItem在其加糖版本中甚至不会覆盖Item任何方法。 In order to make generic types transparent to the JVM, the Java compiler needs to insert these bridge methods which factually override the original implementations in order to bridge the invocations to the methods defined in IntItem . 为了使通用类型对JVM透明,Java编译器需要插入这些桥接方法,这些方法实际上会覆盖原始实现,以便将调用 IntItem定义的方法。 This way, you indirectly override the methods and get the behavior you expect. 这样,您可以间接重写方法并获得预期的行为。 Consider the following scenario: 请考虑以下情形:

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

As mentioned, the IntItem::set(Integer) method is not generic because it is overridden with a non-generically typed method. 如前所述, IntItem::set(Integer)方法不是通用的,因为它被非通用类型的方法覆盖。 Thus, there is no type erasure involved in calling this method. 因此,调用此方法不会涉及类型擦除。 The Java compiler simply compiles the above method call to invoke the method with the byte code signature set(Integer): void . Java编译器只是编译上述方法调用,以使用字节码签名set(Integer): void来调用该方法。 Just like you expected it. 就像您期望的那样。

However, when looking at the following code: 但是,在查看以下代码时:

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

the compiler can not know for sure that the generic variable contains an instance of IntItem or of Item (or any other compatible class). 编译器不能确定generic变量是否包含IntItemItem (或任何其他兼容类)的实例。 Thus, there is no guarantee that a method with the byte code signature set(Integer): void even exists. 因此,无法保证存在带有字节码签名set(Integer): void This is why the Java compiler applies a type erasure and looks at Item<Integer> as if it was a raw type. 这就是Java编译器应用类型擦除并查看Item<Integer> ,就好像它是原始类型一样。 By looking at the raw type, the method set(Object): void is invoked which is defined on Item itself and therefore always exists. 通过查看原始类型,将调用set(Object): void方法,该方法在Item本身上定义,因此始终存在。

As a consequence, the IntItem class can not be sure if its methods are invoked using the method with the erased type (which it inherits from Item ) or the methods with the explicit type. 结果,不能确定IntItem类是使用具有擦除类型(从Item继承来的)的方法还是具有显式类型的方法来调用其方法。 Bridge methods are therefore implemented implicitly to create a single version of truth . 因此,隐式实现了桥接方法以创建单个版本的真理 By dynamically dispatching the bridge, you can override set(Integer) in a subclass of IntItem and the bridge methods still work. 通过动态调度桥,可以在IntItem的子类中重写set(Integer) ,并且桥方法仍然有效。

Bridge methods can also be used for implementing a covariant return type of a method. 桥接方法也可以用于实现方法的协变返回类型。 This feature was added when generics were introduced because bridge methods already existed as a concept. 引入泛型时添加了此功能,因为桥接方法已经作为一个概念存在。 This feature came for free, so to speak. 可以说,此功能是免费提供的。 There is a third application of bridge methods where they implement a visibility bridge . 桥方法的第三种应用是实现可见性 This is necessary to overcome an access restriction of the reflection engine. 这是克服反射引擎的访问限制所必需的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM