簡體   English   中英

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.

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