Here's a puzzle: what does the following code print:
itx = new ArrayList ().iterator(); Method method = itx.getClass().getMethod("hasNext"); System.out.println(method.invoke(itx));
You might expect it to print
false: you're invoking the
hasNext() method on an iterator over an empty list. Instead, you get a rather confusing exception::
Exception in thread "main" java.lang.IllegalAccessException: Class com.example.ReflectionFailure can not access a member of class java.util.AbstractList$Itr with modifiers "public" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at com.example.ReflectionFailure.main(ReflectionFailure.java:16)
IllegalAccessException indicates that the program doesn't have access to the method. But
hasNext() is a public method, how can I not have access to it? The following code compiles and runs without a problem, aren't I doing the same thing with reflection?
itx = new ArrayList ().iterator(); System.out.println(itx.hasNext());
Well, not exactly. If you look at the bytecode of the explicit example, you'll see that you're actually invoking
Iterator.hasNext(), not the method defined by the implementation class:
15: invokeinterface #32, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
And if you change the reflective code so that you get
hasNext() from the
Iterator class, it runs without a problem:
itx = new ArrayList ().iterator(); Method method = Iterator.class.getMethod("hasNext"); System.out.println(method.invoke(itx));
But the first piece of code isn't doing that. Instead, it's trying to do something like the following:
.Itr itx = (AbstractList .Itr)new ArrayList ().iterator();
That code won't compile, because
AbstractList.Itr is a private class; the compiler rightly complains that it isn't visible.
I'm not the first person to stumble over this. There are a number of bugs entered about this problem, although they seem to focus on the use of an inner class. The problem still manifests even if you have a top-level protected class in another package. There's also a posting on the Java Specialists blog that covers the problem and its workaround:
itx = new ArrayList ().iterator(); Method method = Iterator.class.getMethod("hasNext"); method.setAccessible(true); System.out.println(method.invoke(itx));
Method.setAccessible() tells the JVM to allow the calling program to bypass some of the normal security measures; its brother,
Field.setAccessible() is a mainstay of injection frameworks and bean introspecters. There's only one problem: if you're running in a sandbox you aren't allowed to tell the JVM to ignore its safety features, you'll get a
The only always-usable solution is to figure out where the method is actually defined, and use the
Method corresponding to that definition. This is not a trivial task: you have to walk the class hierarchy, looking at all of the concrete or abstract (but still accessible) classes in the hierarchy, as well as all of the interfaces that they implement.
So is this a bug in the JVM? If you couldn't use reflection to accomplish something that you could in explicit code, I'd say yes. In this case, however, you aren't mimicking actual code, you just think you are. So, while inconvenient, in my opinion not a bug.
I've been researching how to determine whether or not a
Method can be invoked without an access error. I assumed that the
isAccessible() would work: if it returned
true, I was good to go. I quickly learned that it always returns
false, unless you've specifically called
setAccessible(true). Not, in my opinion, terribly useful.
I've combed through the documentation several times, but can't find any method that will tell me if I can invoke a
Method. I suppose this is reasonable: accessibility depends on the object doing the invoking. This means that any “find the accessible method” utility would have to take the invoking class as an argument, and there would be no guarantee that the returned method could be invoked by an instance of another class. Not worth pursuing.