Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with constructors of nested class


This question is about interesting behavior of Java: it produces additional (not default) constructor for nested classes in some situations.

This question is also about strange anonymous class, which Java produces with that strange constructor.


Consider the following code:

package a;

import java.lang.reflect.Constructor;

public class TestNested {    
    class A {    
        A() {
        }   

        A(int a) {
        }
    }    

    public static void main(String[] args) {
        Class<A> aClass = A.class;
        for (Constructor c : aClass.getDeclaredConstructors()) {
            System.out.println(c);
        }

    }
}

This will prints:

a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)

Ok. Next, lets make constructor A(int a) private:

    private A(int a) {
    }

Run program again. Receive:

a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)

It is also ok. But now, lets modify main() method in such way (addition of new instance of class A creation):

public static void main(String[] args) {
    Class<A> aClass = A.class;
    for (Constructor c : aClass.getDeclaredConstructors()) {
        System.out.println(c);
    }

    A a = new TestNested().new A(123);  // new line of code
}

Then input becomes:

a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)
a.TestNested$A(a.TestNested,int,a.TestNested$1) 

What is it: a.TestNested$A(a.TestNested,int,a.TestNested$1) <<<---??

Ok, lets again make constructor A(int a) package local:

    A(int a) {
    }

Rerun program again (we don't remove line with instance of A creation!), output is as in the first time:

a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)

Questions:

1) How this could be explained?

2) What is this third strange constructor?


UPDATE: Investigation shown following.

1) Lets try to call this strange constructor using reflection from other class. We will not able to do this, because there isn't any way to create instance of that strange TestNested$1 class.

2) Ok. Lets do the trick. Lets add to the class TestNested such static field:

public static Object object = new Object() {
    public void print() {
        System.out.println("sss");
    }
};

Well? Ok, now we could call this third strange constructor from another class:

    TestNested tn = new TestNested();
    TestNested.A a = (TestNested.A)TestNested.A.class.getDeclaredConstructors()[2].newInstance(tn, 123, TestNested.object);

Sorry, but I absolutely don't understand it.


UPDATE-2: Further questions are:

3) Why Java use special anonymous inner class for an argument type for this third synthetic constructor? Why not just Object type, of constructor with special name?

4) What Java could use already defined anonymous inner class for those purposes? Isn't this some kind of violation of security?

like image 893
Andremoniy Avatar asked Jan 10 '13 19:01

Andremoniy


People also ask

Can nested classes have constructors?

They have access to both static and non-static members in the enclosing context. They can only define instance members. They're the only type of nested classes that cannot define constructors or extend/implement other classes or interfaces.

What are the advantages disadvantages of nested classes?

And the benefit of it is you can have less number of objects created at runtime which wouldn't be the case with other types of nested classes. Disadvantage The only disadvantage I can think of is a static nested class has access to both the protected and private members of the outer class.

Can we write nested constructor in Java?

Now you can create NestedClassExtension instances in this way: NestedClassExtension extension = new NestedClassExtension(new MainClass("main"), "nested"); So the main class has to exist, and its constructor it is called first. Then the constructors of the nested classes.

Are constructor Cannot be provided for which of the following inner classes?

You can declare fields and methods inside an anonymous class, but you cannot declare a constructor. You can declare a static initializer for the anonymous class instead, though.


1 Answers

The third constructor is a synthetic constructor generated by the compiler, in order to allow access to the private constructor from the outer class. This is because inner classes (and their enclosing classes' access to their private members) only exist for the Java language and not the JVM, so the compiler has to bridge the gap behind the scenes.

Reflection will tell you if a member is synthetic:

for (Constructor c : aClass.getDeclaredConstructors()) {
    System.out.println(c + " " + c.isSynthetic());
}

This prints:

a.TestNested$A(a.TestNested) false
private a.TestNested$A(a.TestNested,int) false
a.TestNested$A(a.TestNested,int,a.TestNested$1) true

See this post for further discussion: Eclipse warning about synthetic accessor for private static nested classes in Java?

EDIT: interestingly, the eclipse compiler does it differently than javac. When using eclipse, it adds an argument of the type of the inner class itself:

a.TestNested$A(a.TestNested) false
private a.TestNested$A(a.TestNested,int) false
a.TestNested$A(a.TestNested,int,a.TestNested$A) true

I tried to trip it up by exposing that constructor ahead of time:

class A {    
    A() {
    }   

    private A(int a) {
    }

    A(int a, A another) { }
}

It dealt with this by simply adding another argument to the synthetic constructor:

a.TestNested$A(a.TestNested) false
private a.TestNested$A(a.TestNested,int) false
a.TestNested$A(a.TestNested,int,a.TestNested$A) false
a.TestNested$A(a.TestNested,int,a.TestNested$A,a.TestNested$A) true
like image 57
Paul Bellora Avatar answered Sep 22 '22 18:09

Paul Bellora