简体   繁体   English

内部 Lambda 在 Class.getDeclaredMethods() 中返回?

[英]Inner Lambda getting returned in Class.getDeclaredMethods()?

Consider this class:考虑这个 class:

public class Handler
{
    private Supplier<Foo> foo;

    public void handle( Bar bar )
    {
        foo = () -> bar.getFoo();
    }
}

And consider this reflection snippet which wants to access the handle() method.并考虑这个想要访问 handle() 方法的反射片段。

for( Method method : Handler.class.getDeclaredMethods() )
{
    if ( method.getParameterCount() == 1 && Bar.class.isAssignableFrom( method.getParameterTypes()[0] ) )
    {
        // This is the method you are looking for
    }
}

Instead of finding而不是寻找

  • public void Handler.handle(Bar)

It finds它发现

  • private Foo Handler.lambda$3(Bar)

Which obviously then throws the Exception:这显然会引发异常:

java.lang.IllegalAccessException: class HandlerService cannot access a member of class Handler with modifiers "private static"

Can someone explain what is going on here, please?有人可以解释一下这里发生了什么吗?

It looks like Java considers the lambda inside the method as a top-level declared method.看起来 Java 将方法内部的 lambda 视为顶级声明方法。 Is this new (or even a bug) in Java 11?这是 Java 11 中的新内容(甚至是错误)吗?

You have to be careful with assumptions about the members of the compiled class.您必须小心对已编译 class 成员的假设。

There are even compiler-generated members which are part of the accessible API, like the default constructor or the values() and valueOf(String) methods of enum types.甚至还有编译器生成的成员,它们是可访问的 API 的一部分,例如默认构造函数或enum类型的values()valueOf(String)方法。 Further, compiled constructors of inner classes and enum types may have more parameters than visible in the source code and due to type erasure , the signatures in the compiled methods may differ from the source code.此外,内部类和枚举类型的编译构造函数可能具有比源代码中可见的更多参数,并且由于类型擦除,编译方法中的签名可能与源代码不同。

Besides that, there can be different synthetic members.除此之外,可以有不同的合成成员。 From Java 1.1 to Java 10, nested classes may access each others private members via synthetic helper methods (which became obsolete with Java 11).从 Java 1.1 到 Java 10,嵌套类可以通过合成辅助方法(Java 11 已经过时)访问彼此的私有成员。 Also, overriding methods of generic classes or using covariant return types may cause the generation of a synthetic bridge method.此外,覆盖泛型类的方法或使用协变返回类型可能会导致生成合成桥方法。

And that's still not all.这还不是全部。

The following program以下程序

import java.util.Arrays;
import java.util.stream.Stream;

public enum ShowSyntheticMembers {
    ;
    public static void main(String[] args) {
        Stream.of(ShowSyntheticMembers.class, Inner.class)
            .flatMap(cl -> Stream.concat(Arrays.stream(cl.getDeclaredFields()),
                                         Arrays.stream(cl.getDeclaredMethods())))
            .forEach(System.out::println);
    }
    private boolean x;
    class Inner {
        protected String clone() {
            assert x;
            return "";
        }
    }
}

prints when compiled with JDK 11:使用 JDK 11 编译时打印:

private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
private static void ShowSyntheticMembers.lambda$main$1(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException

while compiling and running with JDK 8 yields在使用 JDK 8 编译和运行时产生

private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
static boolean ShowSyntheticMembers.access$000(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
  • $VALUES is an artifact of the compiler generated values() implementation. $VALUES是编译器生成的values()实现的工件。
  • $assertionsDisabled part of the implementation of the assert statement. $assertionsDisabled部分执行assert语句。
  • this$0 is the inner class' implicit reference to its outer this . this$0是内部类对其外部this的隐式引用。
  • The clone() method with the return type Object , is a bridge method.返回类型为Objectclone()方法是一种桥接方法。
  • The access$000 method helps to access the private field of the outer class from the inner class, which is needed prior to JDK 11. access$000方法有助于从内部 class 访问外部 class 的private字段,这在 JDK 11 之前是必需的。
  • Interestingly, the synthetic method lambda$main$1 , which only exists in the JDK 11 compiled version is part of the System.out::println method reference, but actually not needed here.有趣的是,仅存在于 JDK 11 编译版本中的合成方法lambda$main$1System.out::println方法参考的一部分,但实际上这里不需要。
    It's a side effect of a fix for certain intersection type related issues, hence, very compiler-specific.这是修复某些交集类型相关问题的副作用,因此非常特定于编译器。 Changing .flatMap(…) to .<Object>flatMap(…) in the source code would make the method disappear even with this specific compiler version.在源代码中将.flatMap(…)更改为.<Object>flatMap(…)会使该方法消失,即使使用此特定编译器版本也是如此。

So, since a lot of factors determine the presence of synthetic members not visible in the source code, you should not search for a particular method by only using the parameter type as criteria.因此,由于许多因素决定了源代码中不可见的合成成员的存在,因此您不应仅使用参数类型作为条件来搜索特定方法。

When you want to access the public members, you better use Handler.class.getMethods() instead of Handler.class.getDeclaredMethods() .当您想访问public成员时,最好使用Handler.class.getMethods()而不是Handler.class.getDeclaredMethods() Or use Handler.class.getMethod("handle", Bar.class) to directly get the intended method.或者使用Handler.class.getMethod("handle", Bar.class)直接获取想要的方法。

If you don't want to hardcode the method name as a string, a runtime visible annotation could help to identify the right method.如果您不想将方法名称硬编码为字符串,则运行时可见注释可以帮助识别正确的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM