[英]Inner Lambda getting returned in Class.getDeclaredMethods()?
[英]Class.getDeclaredMethods() of reflection peculiar behavior
我有一個抽象類 A,類 B 是擴展 A 的具體類。
除了類 B 之外,調用 B.class.getDeclaredMethods() 還返回類 A 的方法簽名,但 JAVA 文檔在getDeclaredMethods()
上說有些不同
“這包括公共、受保護、默認(包)訪問和私有方法,但不包括繼承的方法。”
所以從上面的文檔我期待從抽象父類繼承的方法 foo() 不應該從getDeclaredMethods()
調用返回,但我得到從抽象父類繼承的方法 foo() 是從getDeclaredMethods()
調用返回的.
import java.lang.reflect.*;
public class B extends A {
public static void main(String[] args) throws Exception {
Method[] methods = B.class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
}
}
abstract class A {
public void foo() {
}
}
有人可以向我解釋這種行為嗎?
你得到這個的原因是因為超類具有包級別的訪問權限。 如果您將類A
的訪問修飾符更改為public
(您需要將其放入其自己的文件中),則B.class.getDeclaredMethods()
的額外方法將消失。
(另請注意,在A
類上修改的abstract
是紅鯡魚:當A
類不是抽象時,也會發生同樣的事情)
這是Java 編譯器中針對反射錯誤的解決方法:盡管foo
是一個公共方法,但它是在包作用域類A
定義A
。 您可以反思B
類,找到該方法,嘗試使用反射來調用它,結果卻得到了IllegalAccessException
。
編譯器將在類B
生成橋接方法,以便您可以正確地反射調用方法foo
。
如果您將A
的方法foo
設為final
方法,則最好證明這一點,這使得無法修復此反射錯誤(無法覆蓋該方法)
A
類和B
類在abc
包中, C
類在def
包中。 類C
嘗試反射性地調用公共類B
上的方法foo
,但它失敗了,因為它是在非公共類A
定義A
。
線程“main”中的異常java.lang.IllegalAccessException:類def.C無法訪問帶有修飾符“public final”的類abc.A的成員
package abc;
public class B extends A {
}
class A {
public final void foo() {
}
}
package def;
import java.lang.reflect.Method;
import abc.B;
public class C {
public static void main(String[] args) throws Exception {
Method m = B.class.getMethod("foo");
m.invoke(new B());
}
}
只需從方法foo
刪除final
關鍵字即可解決問題,因為編譯器隨后在類B
插入了合成橋接方法。
在此錯誤報告中對此進行了解釋:
http://bugs.java.com/view_bug.do?bug_id=6342411
描述
下面的程序在運行時失敗並出現此錯誤:
Exception in thread "main" java.lang.IllegalAccessException: Class refl.ClientTest can not access a member of class refl.a.Base with modifiers "public" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:578) at refl.ClientTest.main(ClientTest.java:9)
========== test/refl/a/Base.java ========== 1 package refl.a; 2 3 class Base { 4 public void f() { 5 System.out.println("Hello, world!"); 6 } 7 } ========== test/refl/a/Pub.java ========== 1 package refl.a; 2 3 public class Pub extends Base {} ========== test/refl/ClientTest.java ========== 1 package refl; 2 import refl.a.*; 3 import java.lang.reflect.*; 4 5 public class ClientTest { 6 public static void main(String[] args) throws Exception { 7 Pub p = new Pub(); 8 Method m = Pub.class.getMethod("f"); 9 m.invoke(p); 10 } 11 }
評估
建議是在這些非常罕見的情況下添加橋接方法,以解決反射中的問題,而沒有其他可預見的修復或解決方法。 具體來說,當公共方法從非公共類繼承到公共類時,我們將生成橋接方法。
由於其他答案列出的原因,有時編譯器必須在您的類文件中添加一些棘手的代碼; 這可以是字段、構造函數或方法的形式。 但是,它始終將這些字段標記為synthetic
。 這是它添加的實際修飾符,您可以檢查該方法是否與該方法合成:
method.isSynthetic()
因此,無論何時獲得所有方法,請使用此方法過濾列表以僅選擇您在源中實際聲明的方法;)
合成代碼的其他示例包括:自動添加的默認構造函數、對字段中外部類的引用(如果您有非靜態內部類)。
奇怪的地方不在getDeclaredMethods()
- 它在B
的類文件中,其主體僅調用super.foo()
。
我不完全理解它,但它似乎與foo()
是在包私有超類中聲明的公共方法有關。
一些測試用例:
A
package-private, foo
public (as per question): 方法在B
生成A
package-private, foo
package-private: 方法不在B
生成A
public, foo
public: 方法不在B
生成A
public, foo
package-private: 方法不在B
生成我懷疑這個想法是不同包中的第三個類不能“看到” A
,但A.foo()
仍然是public
,所以它應該 (?) 可以通過B
訪問。 為了使其可訪問, B
需要“重新聲明”它。
我不清楚這實際上是正確的 - 上面的(?)。 JLS 6.6.1 聲明(強調我的):
引用類型的成員(類、接口、字段或方法)或類類型的構造函數,只有在該類型可訪問且成員或構造函數聲明為允許訪問時才可訪問
但是這個代碼是允許在不同的包:
B b = new B();
b.foo();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.