为什么'T.super.toString()'和'super :: toString'使用合成访问器方法? [英] Why 'T.super.toString()' and 'super::toString' use a synthetic accessor method?

查看:137
本文介绍了为什么'T.super.toString()'和'super :: toString'使用合成访问器方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下一组表达式:

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 * / 生成并使用合成访问方法 访问$ 0 ?我希望行 / * 2 * / 和行 / * 3 * / 和<$ c的引导方法$ c> / * 4 * / 也使用 invokespecial 作为行 / * 1 * / 确实。

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.

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

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

然而, a差异:

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";}
}

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

Which brings another question: Is there a way how to bypass an override in ((Function<Object, ?> Object::toString)::apply?

推荐答案

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

An invocation of the form super.method() allows to bypass an overriding method() in the same class, invoking the most specific method() of the super class hierarchy. Since, on the byte code level, only the declaring class itself can ignore its own overriding method (and potential overriding methods of subclasses), a synthetic accessor method will be generated if this kind of invocation should be performed by a different (but conceptionally entitled) class, like one of its inner classes, using the form Outer.super.method(...), or a synthetic class generated for a method reference.

请注意,即使班级没有重写被调用的方法,它似乎没有区别,调用必须以这种方式编译,因为在运行时可能有子类覆盖方法,然后,它会有所作为。

Note that even if a class doesn't override the invoked method and it seems to make no difference, the invocation has to be compiled this way as there could be subclasses at runtime overriding the method and then, it will make a difference.

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

It's interesting that the same thing happens when using T.super.method() when T actually isn't an outer class but the class containing the statement. In that case, the helper method isn't really necessary, but it seems that the compiler implements all invocations of the form identifier.super.method(...) uniformly.

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

As a side note, Oracle's JRE is capable of circumventing this byte code restriction when generating classes for lambda expressions/method references, thus, the accessor methods are not needed for method references of the super::methodName kind, which can be shown as follows:

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";
    }
}

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

The generated Supplier will return something alike LambdaSuper@6b884d57 showing that it invoked the overridden Object.toString() method rather than the overriding LambdaSuper.toString(). It seems that the compiler vendors are careful regarding what to expect from the JRE capabilities and, unfortunately, this part seems to be a bit underspecified.

仍然,对于真正的内部类方案,他们 是必需的。

Still, for real inner class scenarios, they are required.

这篇关于为什么'T.super.toString()'和'super :: toString'使用合成访问器方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆