benf.org :  other :  cfr :  inner class fake friends

If you come from c++, you'll know about the 'friend' operator - if class A enfriends class B, class B can see A's private parts....

Java has no such operator. Given that inner classes are actually pulled out into seperate classes, how does an inner class then interact with the outer class?

Synthetic 'this'

(Note that depending on version, you may need to specify --removeinnerclasssynthetics false to CFR, otherwise the syntactic sugar will be hidden)

When we compile the following

public class InnerClassTest0 {

    public void foo() {
        new Inner1();
    }

    public class Inner1 {
        public Inner1() {
        }
    }
}

We will get two classes - InnerClassTest0, and InnerClassTest0$Inner1

CFR decompilation (with --removeinnerclasssynthetics false --innerclasses false)

public class InnerClassTest0
{
    // Methods

    public InnerClassTest0(){
        super();
    }

    public void foo(){
        new InnerClassTest0$Inner1(this);
    }

}

public class InnerClassTest0$Inner1
{
    // Fields
    final /* synthetic */ InnerClassTest0 this$0;
    // Methods

    public InnerClassTest0$Inner1(InnerClassTest0 unnamed_local_this_1){
        this.this$0 = unnamed_local_this_1;
        super();
    }

}

Note that InnerClassTest0$Inner1 (as it is not static) has an explicit 'this' pointer to the outer class. That's what you access when you refer to InnerClassTest0.this.XXX from the inner class. The this$0 pointer is 'synthetic' - it's been introduced by the compiler.

'Friend' access

Given the above, if Inner1 wants to access a member in InnerClassTest0, it has to fake friendship somehow... (unless it's public!).

Consider:

public class InnerClassTest9 {

    public int x = 3;

    public class Inner1 {

        public int tweakX(int y) {
            x += y;
            return x;
        }

    }
}

.... which decompiles to (again, using --removeinnerclasssynthetics false)...

public class InnerClassTest9
{
    // Fields
    public int x;
    // Methods

    public InnerClassTest9(){
        super();
        this.x = 3;
    }

    public class InnerClassTest9$Inner1
    {
        // Fields
        final /* synthetic */ InnerClassTest9 this$0;
        // Methods

        public InnerClassTest9$Inner1(InnerClassTest9 unnamed_local_this_1){
            this.this$0 = unnamed_local_this_1;
            super();
        }

        public int tweakX(int y){
            v4 = this.this$0;
            v4.x = (v4.x + y);
            return this.this$0.x;
        }

    }

}

Fine - the inner class can access outer class's 'x' directly, and so it does so. But what if 'x' is private? In this case, the compiler needs to move usages of 'x' into the outer class, and make sure the inner class interacts with it via public synthetic methods!

public class InnerClassTest8
{
    // Fields
    private int x;
    // Methods

    public InnerClassTest8(){
        super();
        this.x = 3;
    }

    static /* synthetic */ int access$012(InnerClassTest8 x0, int x1){
        v13 = x0;
        v13.x = (v13.x + x1);
        return v13.x;
    }

    static /* synthetic */ int access$000(InnerClassTest8 x0){
        return x0.x;
    }

    public class InnerClassTest8$Inner1
    {
        // Fields
        final /* synthetic */ InnerClassTest8 this$0;
        // Methods

        public InnerClassTest8$Inner1(InnerClassTest8 unnamed_local_this_1){
            this.this$0 = unnamed_local_this_1;
            super();
        }

        public int tweakX(int y){
            InnerClassTest8.access$012(this.this$0, y);
            return InnerClassTest8.access$000(this.this$0);
        }

    }

}

You can see that the compiler has had to generate two extra sythetic 'access' methods in order to provide methods which the inner class can use to access the outer instance.

Inner class fake constructor friends.

The above technique is all well and good if you already have an object to call a public access$000 method on - But what if you're trying to construct an inner class using a private constructor?

public class InnerClassTest32 {

    private class Bob {
        private int get() {
            return 1;
        }
    }

    public int foo() {
        return new Bob().get();
    }

}

Let's look at what we get when we disable removeinnerclasssynthetics.

/*
 * Decompiled with CFR 0_86. (inner class synthetics disabled with removeinnerclasssynthetics false)
 */
package org.benf.cfr.tests;

public class InnerClassTest32 {
    public int foo() {
        return Bob.access$100(new Bob(this, null));
    }

    class 1 {
    }

    class Bob {
        final /* synthetic */ InnerClassTest32 this$0;

        private Bob(InnerClassTest32 innerClassTest32) {
            this.this$0 = innerClassTest32;
        }

        private int get() {
            return 1;
        }

        /* synthetic */ Bob(InnerClassTest32 innerClassTest32, 1 varnull) {
            this(innerClassTest32);
        }

        static /* synthetic */ int access$100(Bob bob) {
            return bob.get();
        }
    }

}

As well as the access method discussed previously, there's a pointless looking class '1'. This exists in order that the actual construction of bob can happen against a public method (ok, package scoped here), however the constructor you wish to remain private is still private.

.... all because the JVM won't let you have friends... aww....


Last updated 08/2014