jvm javac会生成静态桥方法吗?

x6yk4ghg  于 2023-05-28  发布在  Java
关注(0)|答案(4)|浏览(192)

java中使用桥方法来处理派生方法中的协方差,并更改派生方法的可见性。
但是,这两种情况都是针对instance方法的(因为不能派生静态方法)。
我正在研究Kotlin如何生成参数默认值,我惊讶地发现它使用了static桥接方法。

  • 我想不出Javac生成静态桥方法的情况-其他人可以吗?* (这里,我指的是设置了ACC_BRIDGE标志(0x 40)的方法,而不仅仅是语义桥接方法)

(fwiw- 示例代码和反编译(使用cfr 0_124和--hidebridgemethods false)

差异

public class BridgeTest1Base<T> {
 public T frob() {
    return null;
 }
}

public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
 public Integer frob() {
    return null;
 }
}

反编译为

public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
 @Override
 public Integer frob() {
    return null;
 }

 @Override
 public /* bridge */ /* synthetic */ Object frob() {
    return this.frob();
 }
}

可见度

class BridgeTest2Base {
    public void frob() {
    }
}

public class BridgeTest2Derived extends BridgeTest2Base {}

反编译为

public class BridgeTest2Derived extends BridgeTest2Base {
 @Override
 public /* bridge */ /* synthetic */ void frob() {
    super.frob();
 }
}

Kotlin默认值- yum!

class frob2() {

fun fred2(x: Int = 300, y: frob2 = mkFrob2(x)) {
    println("{this}{x}{y}")
}

fun mkFrob2(x: Int): frob2 {
    return this;
}

fun foobar() {
    fred2();
    fred2(100);
    fred2(100, frob2());
}
}

反编译(到java)到(注意静态桥)

public final class frob2 {
public final void fred2(int x, @NotNull frob2 y) {
    Intrinsics.checkParameterIsNotNull((Object)y, (String)"y");
    String string = "{this}{x}{y}";
    System.out.println((Object)string);
}

public static /* bridge */ /* synthetic */ void fred2$default(frob2 frob22, int n, frob2 frob23, int n2, Object object) {
    if ((n2 & 1) != 0) {
        n = 300;
    }
    if ((n2 & 2) != 0) {
        frob23 = frob22.mkFrob2(n);
    }
    frob22.fred2(n, frob23);
}

@NotNull
public final frob2 mkFrob2(int x) {
    return this;
}

public final void foobar() {
    frob2.fred2$default(this, 0, null, 3, null);
    frob2.fred2$default(this, 100, null, 2, null);
    this.fred2(100, new frob2());
}
}
byqmnocz

byqmnocz1#

根据Java语言规范,桥接方法应该使用ACC_BRIDGE进行注解,以确保 override compatible signature,因此使用原始签名调用方法的代码将在被覆盖的方法处结束,即使它在字节码级别具有不同的方法签名。Java编程语言中唯一的应用是类型擦除和协变返回类型。
由于static方法不能以调用者可能被重定向的方式被重写,因此在Java语言规范的意义上,static方法不会出现桥方法。因此,javac永远不会产生一个包含ACC_BRIDGEACC_STATIC的方法。
代表另一种语言的语义将方法标记为桥方法也是一种非常可疑的行为。正如JVM规范所说:
ACC_BRIDGE标志用于指示由Java编程语言的编译器生成的桥方法。
还有其他合成委托方法可能是static,比如嵌套类访问器或方法引用的适配器(例如for varargs or intersection types)。这些方法不算作桥接方法。

dpiehjr4

dpiehjr42#

AFAIK javac目前不会将桥方法生成为 static,这并不意味着将来不会有桥方法是静态的(尽管我不知道甚至是假设的例子)。
桥接方法用于两种情况:当你处理泛型时,正如你所展示的,当重写时,协变返回类型。
在第一种情况下,桥方法是在实现泛型接口或扩展泛型类时创建的。
对于接口,当你覆盖一个泛型方法(必须是非静态的)时,会创建一个桥方法,但是因为它会委托给 * 非静态 * 方法,所以它本身也是非静态的。Java-8允许在接口中使用静态方法,但是,由于是静态的,这些方法是不可重写的(它们甚至不能被继承,而不是来自类的静态方法)。
对于泛型类,情况也是一样。只有 instance 方法是可重写的,即使创建了这样一个桥方法,由于它将调用一个非静态方法,它本身也是这样。需要注意的是,类的静态方法是继承的,但它们是不可重写的(与接口方法相反),因此没有桥接方法。
最后一个例子是协变返回类型:

static class Parent {

}

static class Child extends Parent {

}

static class First {
    public Parent go() {
        return new Parent();
    }
}

static class SubFirst extends First {
    @Override
    public Child go() {
        return new Child();
    }
}

这里将在SubFirst中创建一个桥接方法,但是由于您只能覆盖示例方法(我已经说过多少次了?),则桥方法本身不需要是静态的。

pcrecxhr

pcrecxhr3#

是的,javac生成静态桥方法来允许内部类访问私有的外部类方法。
以这个来源为例:

package bridge;

public class C {
    private void doIt() {
    }

    class D {
        {
            doIt();
        }
    }
}

Javap向您展示了生成的静态access$0方法:

$ javap -p -cp target/classes bridge.C
Compiled from "C.java"
public class bridge.C {
  public bridge.C();
  private void doIt();
  static void access$0(bridge.C);
}

它的内容:

static void access$0(bridge.C);
    descriptor: (Lbridge/C;)V
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #17                 // Method doIt:()V
         4: return
sd2nnvve

sd2nnvve4#

参见the tutorial on bridge methods...@Holger
评论83410934_48213426
我简化了这个文件并尝试了一下。

public class Main {
    public static abstract class Node<T> {
        public abstract void setData(T data);
    }

    public static class MyNode extends Node<Integer> {
        private Integer data;

        @Override
        public void setData(Integer data) {
            this.data = data;
        }
    }
}

运行javac Main.javajavap -v 'Main$MyNode'
结果发现ACC_BRIDGE!

public void setData(java.lang.Object);
  descriptor: (Ljava/lang/Object;)V
  flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  Code:
    stack=2, locals=2, args_size=2
        0: aload_0
        1: aload_1
        2: checkcast     #13                 // class java/lang/Integer
        5: invokevirtual #15                 // Method setData:(Ljava/lang/Integer;)V
        8: return
    LineNumberTable:
      line 6: 0

public void setData(java.lang.Integer);
  descriptor: (Ljava/lang/Integer;)V
  flags: (0x0001) ACC_PUBLIC
  Code:
    stack=2, locals=2, args_size=2
        0: aload_0
        1: aload_1
        2: putfield      #7                  // Field data:Ljava/lang/Integer;
        5: return
    LineNumberTable:
      line 11: 0
      line 12: 5

相关问题