java – sun.reflect.Reflection处理抽象枚举时可能出现的错误?

前端之家收集整理的这篇文章主要介绍了java – sun.reflect.Reflection处理抽象枚举时可能出现的错误?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我已经确定了什么是至少不良行为,并且最多是Sun JDK使用抽象方法处理 Java枚举上的反射的错误.我已经搜索了一个bug报告和StackOverflow对这个特殊行为的回答并且干了.当你认为你在如此经常使用且经过仔细测试的代码中发现了这样的问题时,你或多或少总是错的,所以请理智地检查一下并告诉我在哪里弄错了.

代码

请考虑以下代码

A / Greeting.java

package a;

public enum Greeting {
    HELLO {
        @Override
        public void greet() {
            System.out.println("Hello!");
       }
    };
    public abstract void greet();
}

B / EnumTest.java

package b;

import java.lang.reflect.Method;

import a.Greeting;

public class EnumTest {
    public static void main(String[] args) throws Exception {
        Greeting g=Greeting.HELLO;
        Method greet=g.getClass().getMethod("greet");

        System.out.println("Greeting "+g.getClass()+" ...");
        greet.invoke(g);
        System.out.println("Greeted!");
    }
}

另请注意,Greeting和EnumTest包含在不同的包中. (这最终很重要.)

错误

运行此代码时,您希望获得以下输出

Greeting class a.Greeting ...
Hello!
Greeted!

相反,您将获得以下输出

Greeting class a.Greeting$1 ...
Exception in thread "main" java.lang.IllegalAccessException: Class b.EnumTest can not access a member of class a.Greeting$1 with modifiers "public"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:95)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253)
    at java.lang.reflect.Method.invoke(Method.java:594)
    at b.EnumTest.main(EnumTest.java:13)

了解行为

首先,请注意问候是公开的,问候语是公开的. (即使错误消息表明公共访问!)那么发生了什么?

哎呀在这里怎么样?

如果单步执行代码,您会发现最终的“问题”是sun.reflect.Reflection $verifyMemberAccess()返回false. (因此,Reflection API声称我们无法访问此方法.)失败的特定代码位于:

public static boolean verifyMemberAccess(Class currentClass,// Declaring class of field
                                        // or method
                                        Class  memberClass,// May be NULL in case of statics
                                        Object target,int    modifiers)
    // ...

    if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
       isSameClassPackage = isSameClassPackage(currentClass,memberClass);
       gotIsSameClassPackage = true;
       if (!isSameClassPackage) {
           return false;
       }
    }

    // ...

本质上,此方法确定currentClass中的代码是否可以使用修饰符的修饰符来查看memberClass的成员.

显然,我们应该有权访问.我们在公共课堂上称呼公共方法!但是,此代码在指示的return语句中返回false.因此,我们尝试调用方法的值的类不是公共的. (我们知道这是因为外部测试 – !Modifier.isPublic(getClassAccessFlags(memberClass)) – 传递,因为代码到达内部返回.)但问候是公开的!

但是,Greeting.HELLO的类型不是a.Greeting.这是a.Greeting $1! (小心的读者会在上面注意到.)

具有一个或多个抽象方法的枚举类在封面下创建子类(每个常量一个).所以正在发生的事情是“隐蔽”子类没有标记为公共,所以我们不允许在这些类上看到公共方法.游民.

确认理论

为了测试这个理论,我们可以在子代上调用超类enum的greet()方法

public static void main(String[] args) throws Exception {
    Greeting g=Greeting.HELLO;
    Method greet=g.getClass().getSuperclass().getMethod("greet");

    System.out.println("Greeting "+g.getClass()+" ...");
    greet.invoke(g);
    System.out.println("Greeted!");
}

……并取得成功:

Greeting class a.Greeting$1 ...
Hello!
Greeted!

另外,如果我们将a.Greeting移动到b.Greeting(与b.EnumTest相同的包),即使没有getSuperclass()调用,它也可以工作.

那么…… Bug还是No?

那么……这是一个错误吗?或者这只是不希望的行为,是底层实现的工件?我检查了the relevant section of the Java Language Specification,这种语法是合法的.此外,规范没有规定如何安排子类,所以虽然这在技术上违反了标准(或者至少是我读过的标准的一部分),但我倾向于称这是一个错误.

StackOverflow的想法是:这是一个错误,还是仅仅是不受欢迎的行为?我意识到这是一个非传统的问题,所以请原谅格式.

此外,我在Mac上(如果重要的话),并且java -version打印以下内容,对于想要重现的人:

$java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b12)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01,mixed mode)

编辑:有兴趣找到自1997年以来类似(至少相关)问题的错误http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957

编辑:根据下面的答案,JLS确实说使用抽象方法的枚举类应该像匿名类一样:

The optional class body of an enum constant implicitly defines an anonymous class declaration (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes

根据上面的错误,自1997年以来,匿名类处理一直是一个“错误”.所以关于这是否实际上是一个错误在这一点上有点语义.一句话:不要这样做,因为它不起作用,将来不太可能. 原文链接:https://www.f2er.com/java/128709.html

猜你在找的Java相关文章