请考虑以下代码:
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