【问题标题】:Java 9+, calling Map.Entry::getValue via reflectionJava 9+,通过反射调用 Map.Entry::getValue
【发布时间】:2018-05-12 12:11:01
【问题描述】:

我正在使用新的模块化系统,但遇到了反射问题。

在我的代码中,我尝试通过反射调用 Map.Entry 的 getValue 方法。它不允许我这样做。

我已经阅读了一些关于新模块化系统的教程,为什么要设置反射保护,什么是导出和打开,如何以及在哪里声明它们等等。 但是对于这个特定的人,我不确定是否了解所有内容。

这是我能提供的最少代码:

package pkg;
import java.util.*;
import java.lang.reflect.*;

public class Test {
public static void main (String[] args) throws Exception {

Map<String,Object> someMap = new LinkedHashMap<>();
someMap.put("someKey", "someValue");
Map.Entry<String,Object> entry = someMap.entrySet().iterator().next();

System.out.println(entry.getValue()); // normal access is fine

Method getter = entry .getClass() .getMethod("getValue");
getter.setAccessible(true); //1
System.out.println(getter.invoke(entry)); //2 
}}

调用 setAccessible 时,它​​会崩溃 (1) :
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make public final java.lang.Object java.util.HashMap$Node.getValue() accessible: module java.base does not "opens java.util" to module mymodule

注释setAccessible(1)时,调用invoke(2)时崩溃:
Exception in thread "main" java.lang.IllegalAccessException: class pkg.Test (in module mymodule) cannot access a member of class java.util.HashMap$Node (in module java.base) with modifiers "public final"

幸运的是,错误消息非常明确:我可以通过使用命令行参数--add-opens java.base/java.util=mymodule 添加缺少的打开来解决问题。 但这可能不是长期的正确方法。 我在某处读到 --add-opens/exports 无论如何都会随着 Java 11 消失。

难道没有比使用 --add-opens 更好的解决方案吗?

正常/直接/常规访问有效,getValue 方法是公共的,Map.Entry 显然是从 java.base 正确导出的。 那么为什么这个反射调用被阻塞了呢?

感谢您的回答。

【问题讨论】:

    标签: java


    【解决方案1】:

    使用接口的Method 调用getValue 解决了这个问题:你不需要让你的方法可以访问,因为它已经是公开的了:

    Method getter = Map.Entry.class.getMethod("getValue");
    System.out.println(getter.invoke(entry));
    

    Demo 1.

    您不需要对接口进行硬编码 - 反射让您可以在可用的地方搜索它:

    private static Method tryFindInterfaceMethod(Method m) throws Exception {
        for (Class bc : m.getDeclaringClass().getInterfaces()) {
            try {
        return bc.getMethod(m.getName(), m.getParameterTypes());
            } catch (NoSuchMethodException ignore) {
            }
        }
        return m;
    }
    

    现在您可以按如下方式访问 getter:

    Method getter = tryFindInterfaceMethod(entry.getClass().getMethod("getValue"));
    

    Demo 2

    就行为的解释而言,这可能与Java反射中的a very old bug有关。

    【讨论】:

    • 你的demo用的是java 8,而模块是用java 9引入的。你确定这个答案可以接受吗?
    • @YassinHajaj 这是一个基本的反射问题,它在很久以前就以不同的形式表现出来了(详见Cant access to java.util.HashMap$Entry with modifiers “public final”问题)。本质上,它是“访问私有类的公共方法”问题。我很确定,一旦他切换到在Map.Entry 的界面上使用Method 对象,OP 将能够使其工作。
    • 好的,因为java-9 有一种新方法可以在运行时提供包,我认为这与问题有关。
    • 谢谢,从界面获取方法有效。但是,这意味着我必须更改反射代码以添加特殊情况。我遇到这个问题的真正地方当然根本不知道它正在调用实现 Map.Entry 的对象上的方法。那么其他潜在的类似案例呢?我希望得到一个更通用的答案。
    • 好的,我找到了一个解决方案,可能不太理想:在调用方法之前,如果类是私有的,那么我通过实现的接口来查看是否找到同名的方法和参数。还没有尝试过像重载和泛型这样的边缘情况,但在全球范围内它是有效的。所以我可以接受这个答案。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多