gpt4 book ai didi

java - 为什么 'T.super.toString()' 和 'super::toString' 使用合成访问器方法?

转载 作者:搜寻专家 更新时间:2023-10-30 20:02:46 25 4
gpt4 key购买 nike

考虑以下表达式集:

class T {{
/*1*/ super.toString(); // direct
/*2*/ T.super.toString(); // synthetic
Supplier<?> s;
/*3*/ s = super::toString; // synthetic
/*4*/ s = T.super::toString; // synthetic
}}

结果如下:

class T {
T();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 aload_0 [this]
5 invokespecial java.lang.Object.toString() : java.lang.String [10]
8 pop // ^-- direct
9 aload_0 [this]
10 invokestatic T.access$0(T) : java.lang.String [14]
13 pop // ^-- synthetic
14 aload_0 [this]
15 invokedynamic 0 get(T) : java.util.function.Supplier [21]
20 astore_1 [s] // ^-- methodref to synthetic
21 aload_0 [this]
22 invokedynamic 1 get(T) : java.util.function.Supplier [22]
27 astore_1 // ^-- methodref to synthetic
28 return

static synthetic java.lang.String access$0(T arg0);
0 aload_0 [arg0]
1 invokespecial java.lang.Object.toString() : java.lang.String [10]
4 areturn

Bootstrap methods:
0 : # 40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:...
#43 invokestatic T.access$0:(LT;)Ljava/lang/String;
1 : # 40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:...
#46 invokestatic T.access$0:(LT;)Ljava/lang/String;
}

为什么 java代码行/*2*/ , /*3*//*4*/ 生成和使用合成访问器方法 access$0 ?我希望这条线 /*2*/和行的引导方法 /*3*//*4*/也可以使用 invokespecial作为行 /*1*/

特别是当方法Object::toString可直接从相关范围访问,例如以下方法引用不包含对合成访问器方法的调用:

class F {{
Function<Object, ?> f = Object::toString; // direct
}}

但是,有一个区别:

class O {{
super.toString(); // invokespecial -> "className@hashCode"
O.super.toString(); // invokespecial -> "className@hashCode"
Supplier<?> s;
s = super::toString; // invokespecial -> "className@hashCode"
s = O.super::toString; // invokespecial -> "className@hashCode"
Function<Object, ?> f = Object::toString;
f.apply(O.super); // invokeinterface -> "override"
}
public String toString() {return "override";}
}

这带来了另一个问题:有没有办法绕过 ((Function<Object, ?> Object::toString)::apply 中的覆盖?

最佳答案

形式的调用 super.method()允许绕过覆盖 method()在同一个类中,调用最具体的 method()父类(super class)层次结构。因为,在字节码级别,只有声明类本身可以忽略它自己的重写方法(以及子类的潜在重写方法),如果这种调用应该由不同的(但概念上称为) 类,就像它的内部类之一,使用 Outer.super.method(...) 的形式,或为方法引用生成的合成类。

请注意,即使一个类没有覆盖被调用的方法并且看起来没有什么区别,调用也必须以这种方式编译,因为在运行时可能有子类覆盖该方法,然后,它会有所不同.

有趣的是,使用 T.super.method() 时也会发生同样的事情。什么时候T实际上不是外部类,而是包含语句的类。在那种情况下,辅助方法并不是真正必要的,但编译器似乎实现了所有形式的调用 identifier.super.method(...)统一。


作为旁注,Oracle 的 JRE 能够在为 lambda 表达式/方法引用生成类时绕过此字节码限制,因此,super::methodName 的方法引用不需要访问器方法。 kind,可以表示为:

import java.lang.invoke.*;
import java.util.function.Supplier;

public class LambdaSuper {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup l=MethodHandles.lookup();
MethodType mt=MethodType.methodType(String.class);
MethodHandle target=l.findSpecial(Object.class, "toString", mt, LambdaSuper.class);
Supplier<String> s=(Supplier)LambdaMetafactory.metafactory(l, "get",
MethodType.methodType(Supplier.class, LambdaSuper.class),
mt.generic(), target, mt).getTarget().invokeExact(new LambdaSuper());
System.out.println(s.get());
}

@Override
public String toString() {
return "overridden method";
}
}

生成的Supplier将返回类似的东西 LambdaSuper@6b884d57表明它调用了重写的 Object.toString()方法而不是覆盖 LambdaSuper.toString() .似乎编译器供应商对 JRE 功能的期望很谨慎,不幸的是,这部分似乎有点不明确。

不过,对于真正的内部类场景,它们是必需的。

关于java - 为什么 'T.super.toString()' 和 'super::toString' 使用合成访问器方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34675012/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com