Java 为什么';T.super.toString()和#x27;和';超级:toString';使用合成存取器方法?

Java 为什么';T.super.toString()和#x27;和';超级:toString';使用合成存取器方法?,java,java-8,super,method-reference,synthetic,Java,Java 8,Super,Method Reference,Synthetic,考虑以下一组表达式: class T {{ /*1*/ super.toString(); // direct /*2*/ T.super.toString(); // synthetic Supplier<?> s; /*3*/ s = super::toString; // synthetic /*4*/ s = T.super::toString; // synthetic }} 为什么java代码行/*2*/,/*3*/和

考虑以下一组表达式:

class T {{
/*1*/   super.toString();      // direct
/*2*/   T.super.toString();    // synthetic
        Supplier<?> s;
/*3*/   s = super::toString;   // synthetic
/*4*/   s = T.super::toString; // synthetic
}}
为什么java代码行
/*2*/
/*3*/
/*4*/
生成并使用合成访问器方法
访问$0
?我希望行
/*2*/
和行
/*3*/
/*4*/
的引导方法也像行
/*1*/
一样使用
invokespecial

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

class F {{
    Function<Object, ?> f = Object::toString; // direct
}}
F类{{
函数f=Object::toString;//直接
}}
但是,有一个区别:

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";}
}
O类{{
super.toString();//invokespecial->”className@hashCode"
O.super.toString();//调用特殊->”className@hashCode"
供应商;
s=super::toString;//调用特殊->”className@hashCode"
s=O.super::toString;//调用特殊->”className@hashCode"
函数f=对象::toString;
f、 应用(O.super);//调用接口->“覆盖”
}
公共字符串toString(){return“override”;}
}

这带来了另一个问题:有没有一种方法可以绕过
((函数对象::toString)::apply

super.method()
形式的调用允许绕过同一类中的重写
方法()
,调用最具体的
方法()
。由于在字节码级别上,只有声明类本身可以忽略其自身的重写方法(以及子类的潜在重写方法),因此如果此类调用应由不同的(但在概念上有名称)执行,则将生成合成访问器方法类,就像它的一个内部类一样,使用形式
Outer.super.method(…)
,或者为方法引用生成的合成类

请注意,即使一个类没有重写被调用的方法,并且它似乎没有什么不同,调用也必须以这种方式编译,因为在运行时可能有子类重写了该方法,然后,它会产生不同

有趣的是,当使用
T.super.method()
时,当
T
实际上不是一个外部类,而是包含语句的类时,也会发生同样的事情。在这种情况下,helper方法实际上不是必需的,但编译器似乎实现了
identifier.super.method(…)形式的所有调用
统一设置


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

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";
    }
}
import java.lang.invoke.*;
导入java.util.function.Supplier;
公共类LambdaSuper{
公共静态void main(字符串[]args)抛出可丢弃的{
MethodHandles.Lookup l=MethodHandles.Lookup();
MethodType mt=MethodType.MethodType(String.class);
MethodHandle target=l.findSpecial(Object.class,“toString”,mt,LambdaSuper.class);
供应商s=(供应商)LambdaMetafactory.metafactory(l,“获取”,
MethodType.MethodType(Supplier.class,lambdaster.class),
mt.generic(),target,mt.getTarget().invokeExact(新lambdassuper());
System.out.println(s.get());
}
@凌驾
公共字符串toString(){
返回“重写方法”;
}
}
生成的
供应商
将返回类似的
LambdaSuper@6b884d57
显示它调用了重写的
Object.toString()
方法,而不是重写的
lambdassuper.toString()
。编译器供应商似乎对JRE功能的预期很谨慎,不幸的是,这一部分似乎有点不够具体

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

已经解释了它发生的原因-
super
引用仅限于直接的子类。下面是一个更详细的版本:


调用封闭类型的“超类”方法 它生成一系列合成存取器方法:

class T {
    static synthetic String access$0(T t) { // executing accessor
        return t.super.toString(); // only for the refered outer class
    }
    class U { // new U(T.this)
        static synthetic T access$0(U u) { // relaying accessor
            return T.this; // for every intermediate outer class
        }
        class V {{ // new V(U.this)
            T.access$0(U.access$0(U.this)); // T.access$0(T.this)
        }}
    }
}
T
是直接封闭类时,即不存在中间外部类,则在类
T
中只生成“正在执行”的访问器(即,它本身似乎是不必要的)

注意:访问器链是由Eclipse生成的,但不是由OpenJDK生成的,请参见下文


方法引用到自己的超类的方法 这是一个与前一个类似的单数情况,因为
super::toString
在这里等同于
V.super::toString
,所以合成访问器是在类
V
本身中生成的。这里的一个新元素是引导方法,用于将
对象::toString
调整为
供应商::get

注意:这里只有OracleJDK足够“聪明”(如前所述),可以通过将
super
调用直接放入方法引用适配器来避免合成访问器


方法引用封闭类型的“超类”方法 这里没有什么新东西,只需注意,内部类总是只接收直接外部类的实例,因此在类
V
中,使用
T。这个
可能会遍历整个中间合成访问器方法链,例如
U.access$0(V.U\U this)
(如Eclipse中),或利用这些合成字段(参考
outer.this
)的包可见性,并将
T.this
转换为
V.U\U this.T\U this
class T {
    class U {
        class V {{
/*2*/       T.super.toString();
        }}
    }
}
class T {
    static synthetic String access$0(T t) { // executing accessor
        return t.super.toString(); // only for the refered outer class
    }
    class U { // new U(T.this)
        static synthetic T access$0(U u) { // relaying accessor
            return T.this; // for every intermediate outer class
        }
        class V {{ // new V(U.this)
            T.access$0(U.access$0(U.this)); // T.access$0(T.this)
        }}
    }
}
class T {
    class U {
        class V {{
            Supplier<?> s;
/*3*/       s = super::toString;
        }}
    }
}
class T {
    class U {
        class V {
            static synthetic String access$0(V v) {
                return v.super.toString();
            }
            dynamic bootstrap Supplier get(V v) { // methodref
                return () -> V.access$0(v); // toString() adapted to get()
            }
            {
                get(V.this);
            }
        }
    }
}
class T {
    class U {
        class V {{
            Supplier<?> s;
/*4*/       s = T.super::toString;
        }}
    }
}
class T {
    static synthetic String access$0(T t) { // executing accessor
        return t.super.toString(); // only for the refered outer class
    }
    class U { // new U(T.this)
        static synthetic T access$0(U u) { // relaying accessor
            return T.this; // for every intermediate outer class
        }
        class V { // new V(U.this)
            dynamic bootstrap Supplier get(T t) { // methodref
                return () -> T.access$0(t); // toString() adapted to get()
            }
            {
                get(U.access$0(U.this)); // get(T.this)
            }
        }
    }
}
class T {
    static synthetic String access$0(T t) { // executing accessor
        return t.super.toString(); // only for the refered outer class
    }
    class U { // new U(T.this)
        class V { // new V(U.this)
            instance synthetic Object lambda$0() {
                return T.access$0(V.U_this.T_this); // direct relay
            }
            dynamic bootstrap Supplier get(V v) { // methodref
                return () -> V.lambda$0(v); // lambda$0() adapted to get()
            }
            {
                get(V.this);
            }
        }
    }
}