Consider the following set of expressions:
class T {{
/*1*/ super.toString(); // direct
/*2*/ T.super.toString(); // synthetic
Supplier<?> s;
/*3*/ s = super::toString; // synthetic
/*4*/ s = T.super::toString; // synthetic
}}
Which gives the following result:
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;
}
Why java code lines /*2*/
, /*3*/
and /*4*/
produce and use a synthetic accessor method access$0
? I would expect the line /*2*/
and bootstrap methods for lines /*3*/
and /*4*/
to also use invokespecial
as the line /*1*/
does.
Especially when the method Object::toString
is accessible directly from the relevant scope, e.g. the following method reference doesn't wrap a call to a synthetic accessor method:
class F {{
Function<Object, ?> f = Object::toString; // direct
}}
However, there is a difference:
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";}
}
Which brings another question: Is there a way how to bypass an override in ((Function<Object, ?> Object::toString)::apply
?
f.apply(O.super);
can't make a difference tof.apply(O.this);
as it's the same object and the invocation behavior is fixed for this function. You can't create aFunction<Object, ?>
that ignores overrides (with legal Java constructs), but you can create aFunction<O, ?>
that ignores overrides, using a helper method, similar to these syntheticaccess$n
methods. – Frock((Function<O, ?>) O::helper).apply(this)
whereprivate String helper() {return super.toString();}
works fine. However, it works only 1 level up, and unless you create a chain of helpers up the hierarchy, you'll never get a hold of the realObject::toString
, right? Thanks anyway. – Aronprivate String helper() {return OuterMostClass.super.toString();}
it will still only call the parent of theOuterMostClass
, notObject
, so it seems there is no plain java way. – Aroninvokespecial
would do... – Aroninvokespecial
allowed to skip/target arbitrary classes (Java 1.0). Nowadays, for non-private
, non-interface
methods the target type must be the direct superclass of the containing class. Otherwise, the verifier is entitled to reject it. There was a time when the absence of theACC_SUPER
flag could reinforce the old behavior, but the most recent JVMs treat all classes like if the flag is present. – Frock