簡體   English   中英

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

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

考慮這個 class:

public class Handler
{
    private Supplier<Foo> foo;

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

並考慮這個想要訪問 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
    }
}

而不是尋找

  • public void Handler.handle(Bar)

它發現

  • private Foo Handler.lambda$3(Bar)

這顯然會引發異常:

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

有人可以解釋一下這里發生了什么嗎?

看起來 Java 將方法內部的 lambda 視為頂級聲明方法。 這是 Java 11 中的新內容(甚至是錯誤)嗎?

您必須小心對已編譯 class 成員的假設。

甚至還有編譯器生成的成員,它們是可訪問的 API 的一部分,例如默認構造函數或enum類型的values()valueOf(String)方法。 此外,內部類和枚舉類型的編譯構造函數可能具有比源代碼中可見的更多參數,並且由於類型擦除,編譯方法中的簽名可能與源代碼不同。

除此之外,可以有不同的合成成員。 從 Java 1.1 到 Java 10,嵌套類可以通過合成輔助方法(Java 11 已經過時)訪問彼此的私有成員。 此外,覆蓋泛型類的方法或使用協變返回類型可能會導致生成合成橋方法。

這還不是全部。

以下程序

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

使用 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

在使用 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是編譯器生成的values()實現的工件。
  • $assertionsDisabled部分執行assert語句。
  • this$0是內部類對其外部this的隱式引用。
  • 返回類型為Objectclone()方法是一種橋接方法。
  • access$000方法有助於從內部 class 訪問外部 class 的private字段,這在 JDK 11 之前是必需的。
  • 有趣的是,僅存在於 JDK 11 編譯版本中的合成方法lambda$main$1System.out::println方法參考的一部分,但實際上這里不需要。
    這是修復某些交集類型相關問題的副作用,因此非常特定於編譯器。 在源代碼中將.flatMap(…)更改為.<Object>flatMap(…)會使該方法消失,即使使用此特定編譯器版本也是如此。

因此,由於許多因素決定了源代碼中不可見的合成成員的存在,因此您不應僅使用參數類型作為條件來搜索特定方法。

當您想訪問public成員時,最好使用Handler.class.getMethods()而不是Handler.class.getDeclaredMethods() 或者使用Handler.class.getMethod("handle", Bar.class)直接獲取想要的方法。

如果您不想將方法名稱硬編碼為字符串,則運行時可見注釋可以幫助識別正確的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM