How does an annonymous innerclass arrow a private instance variable?

Asked

Viewed 58 times

6

Since Java does not actually have closures, but emulates behavior with a technique using Inner classes, the following code:

class Test{
    private int myField;


    private void doSomething(){
        someInstance.setListener(new onClickListener(){
            @Override
            publicc void onClick(){
                myField = 3;
            }
        });
    }
}

It will, approximately, turn:

class Test{
    private int myField;


    private void doSomething(){
        someInstance.setListener(new $AnonClass(this));
    }
}

class $AnonClass{
    Test test;

    public $AnonClass(Test test){
        this.test = test;
    }

    public void onClick(){
        //Como ele vai setar se a variável é privada???
        //Reflection???
        test.myField = 3;
    }
}

My question is: How will the compiler (or whoever) set the private variable?

I know it’s possible to do this with reflection, but I haven’t found anywhere that actually determines what is done in this scenario.

And taking advantage of the hook, because Java does not implement at once first-order functions and closures?

1 answer

2


After talking with some friends and a deeper research, I found out what is the appearance that happens in this case. It’s kind of obscure and relatively hard to find, so I’ll try to explain.

The core of this behavior is a type of method, which is widely used by Java, which is known as Synthetic Method. According to the Java specification, an element is synthetic when it is produced by the compiler and does not have a corresponding source code, except for standard constructors. There are other types of synthetic structures, but let’s focus on synthetic methods.

So how do these synthetic methods solve the problem of access to a private instance variable?

Well, the java compiler will generate public methods to access ALL variables that are accessed by an innerclass. Let’s look at the previous example.

class Test{
    private int myField;


    private void doSomething(){
        someInstance.setListener(new onClickListener(){
            @Override
            publicc void onClick(){
                myField = 3;
            }
        });
    }
}

We have an anonymous class that is accessing a private instance variable that will turn into something like:

class Test{
    private int myField;


    private void doSomething(){
        someInstance.setListener(new $AnonClass(this));
    }
}

class $AnonClass{
    Test test;

    public $AnonClass(Test test){
        this.test = test;
    }

    public void onClick(){
        //Por enquanto é mágica!
    }
}

And that’s where synthetic methods come in. The compiler will create a public method in the class Test in order to give access to the desired variable, and this method is the famous synthetic method. So, it would look something like this:

class Test{
    private int myField;


    private void doSomething(){
        someInstance.setListener(new $AnonClass(this));
    }

    public int access$0000(int a){
        this.myField = a;
    }
}

class $AnonClass{
    Test test;

    public $AnonClass(Test test){
        this.test = test;
    }

    public void onClick(){
        test.access$0000(3);
    }
}

I made an example that you can see this kind of method being created.

Simple example showing a class with a Main method accessing a private variable from an innerclass.

public class SyntheticMethodTest {
    public static void main(String[] args){
        Test test = new Test(2);
        System.out.println(test.a);

        Class cls = test.getClass();
        for (Method field : cls.getDeclaredMethods()){
            System.out.println(field);
        }
    }

    static class Test{
        private int a;

        public Test(){

        }

        public Test(int a) {
            this.a = a;
        }

        public int getA() {
            return a;
        }

        public void setA(int a) {
            this.a = a;
        }
    }
}

The output is:

2 static int SyntheticMethodTest$Test.access$000(SyntheticMethodTest$Test) public void SyntheticMethodTest$Test.setA(int) public int SyntheticMethodTest$Test.getA()

And there’s the static int SyntheticMethodTest$Test.access$000(SyntheticMethodTest$Test), which is the synthetic method created to access the variable a.

If the line System.out.println(test.a); is removed, method is not created.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.