【发布时间】:2016-04-13 00:22:49
【问题描述】:
考虑以下一组表达式:
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*/ 行的引导方法也像/*1*/ 行一样使用invokespecial。
特别是当方法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 中的覆盖?
【问题讨论】:
-
请注意,lambda 表达式的行为是固定的,不能由调用者更改。因此,无效(但被 Eclipse 接受)语法
f.apply(O.super);不能对f.apply(O.this);产生影响,因为它是同一个对象,并且此函数的调用行为是固定的。您不能创建一个忽略覆盖的Function<Object, ?>(使用合法的Java 结构),但您可以使用一个辅助方法创建一个忽略覆盖的Function<O, ?>,类似于这些合成的access$n方法。 -
经过测试。
((Function<O, ?>) O::helper).apply(this)其中private String helper() {return super.toString();}工作正常。但是,它只能向上工作 1 级,除非您在层次结构上创建一个助手链,否则您将永远无法获得真正的Object::toString,对吗?还是谢谢。 -
对于内部类,当helper为
private String helper() {return OuterMostClass.super.toString();}时,它仍然只会调用OuterMostClass的父类,而不是Object,所以似乎没有普通的java方式。 -
...一个简单的
invokespecial就可以了... -
不,那是很久以前的事了,当时
invokespecial允许跳过/定位任意类(Java 1.0)。如今,对于非private、非interface方法,目标类型必须是包含类的直接超类。否则,验证者有权拒绝它。曾经有一段时间,ACC_SUPER标志的缺失可能会强化旧的行为,但最近的 JVM 将所有类视为是否存在标志。
标签: java java-8 super method-reference synthetic