How does an annonymous innerclass arrow an instance private variable?

5

Given that 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 does the compiler (or whoever it is) go with the private variable?

I know that it is possible to do this with reflection, but I have nowhere to really determine what is done in this scenario.

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

    
asked by anonymous 28.08.2018 / 03:10

1 answer

1

After talking to some friends and taking a deeper look, I found out what the sharing is in this case. It's somewhat obscure and relatively difficult to find, so I'll try hard 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 match in the source code except the default 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 accessing an instance private variable?

Well, the java compiler will generate public methods to access ALL the 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 an instance private variable and will become 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 Test class 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've done an example that you can see this kind of method being created.

Simple example that shows 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 here is static int SyntheticMethodTest$Test.access$000(SyntheticMethodTest$Test) , which is the synthetic method created to access the variable a .

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

    
28.08.2018 / 19:41