views:

101

answers:

3

Problem

One friend suggested an interesting problem. Given the following code:

public class OuterClass {

    private String message = "Hello World";

    private class InnerClass {
        private String getMessage() {
            return message;
        }
    }

}

From an external class, how may I print the message variable content? Of course, changing the accessibility of methods or fields is not allowed.

(the source here, but it is a french blog)


Solution

The code to solve this problem is the following:

try {
    Method m = OuterClass.class.getDeclaredMethod("access$000", OuterClass.class);
    OuterClass outerClass = new OuterClass();
    System.out.println(m.invoke(outerClass, outerClass));
} catch (Exception e) {
    e.printStackTrace();
}

Note that the access$000 method name is not really standard (even if this format is the one that is strongly recommanded), and some JVM will name this method access$0. Thus, a better solution is to check for synthetic methods:

Method method = null;
int i = 0;
while ((method == null) && (i < OuterClass.class.getDeclaredMethods().length)) {
    if (OuterClass.class.getDeclaredMethods()[i].isSynthetic()) {
        method = OuterClass.class.getDeclaredMethods()[i];
    }
    i++;
}
if (method != null) {
    try {
        System.out.println(method.invoke(null, new OuterClass()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

So the interesting point in this problem is to highlight the use of synthetic methods. With these methods, I can access a private field as it was done in the solution. Of course, I need to use reflection, and I think that the use of this kind of thing can be quite dangerous...

Question

What is the interest - for me, as a developer - of a synthetic method? What can be a good situation where using the synthetic can be useful?

A: 

You should never invoke synthetic accessor methods using reflection, they're liable to change, depending on which compiler you use. For example, running your solution on jdk1.6 on my machine failed, since the access$000 method was not found.

Synthetic accessors are an under-the-covers compiler hack to get around the fact that inner classes were an addition to Java 1.1, and the VM spec was never changed to accommodate them.

skaffman
Yes, I'm aware that changing the accessibility of the private field is a "better" solution. However, as stated in the problem *changing the accessibility of methods or fields is not allowed*.
romaintaz
@romaintaz: Sorry, I misunderstood you, I thought you meant changing the access modifiers. I've removed the bit of the answer, the rest of it still stands.
skaffman
+1  A: 

As you demonstrate, Java access modifiers are merely informative, and they can be circumvented by using reflection. So your questions is roughly equivalent to "what is the interest of circumventing an access modifier?" Apart from malicious purposes, debugging comes to mind; you could for instance log the internal state of some library's internals from outside the library, without having to touch the library code itself at all. Another example is co-operation with scripting languages; if you don't know on compile-time which classes and methods are available, reflection can be quite useful. For instance, Jython's internals uses huge amounts of reflection all around.

Joonas Pulakka
+1  A: 

What is the interest - for you, as a developer - of a synthetic method? Well, for starters, don’t invoke them (for reasons other answerers have explained).

But there is one interesting thing to keep in mind, particularly if you're writing Java code to run in a security-sensitive environment, and that's that synthetic methods can potentially provide attackers a back-door to your private fields.

Of course, there are some big "ifs"--I mean, necessary conditions--for such a vulnerability to actually matter:

  1. The attacker actually has to be running code in your JVM. Most of the time this is not an issue, because the only code running is your own.
  2. The attacker's code needs to be in the same package as your inner class (because the synthetic method is declared package-private).
  3. The attacker's code must be loaded from the same classloader as your class. If you're letting untrusted code to run in your JVM (condition #1) then you'd better use a separate classloader for the untrusted code.
Will