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

标签 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
}}

结果如下:

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/

相关文章:

java - CompletableFuture runAsync 与使用 Executor 执行每个 Runnable

java - 使用 lambda 表达式反转字符串中的单词

javascript - 如果没有任何参数,super() 会做什么?

python - 创建从 super() 类中提取变量的子类时出现问题

java - 第二次调用时如何访问 Spring Controller 方法中的参数

java - $avg 未返回平均值

java - 如何修复 java.lang.NoSuchMethodError : sun. security.ssl.SSLSessionImpl

perl - 将SUPER引用到对象的父类(super class)

用于交互式 SSH session 的 Java 库(能够执行多部分命令)?

java - append 到文件时如何关闭 JavaFX FileChooser 的覆盖警告?