Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When an anonymous class with no references to its enclosing class is returned from an instance method, it has a reference to this. Why?

When an anonymous class with no references to its enclosing class is returned from an instance method, it has a reference to this. Why?

Consider the following code:

package so;

import java.lang.reflect.Field;

public class SOExample {

    private static Object getAnonymousClassFromStaticContext() {
        return new Object() {
        };
    }

    private Object getAnonymousClassFromInstanceContext() {
        return new Object() {
        };
    }

    public static void main(String[] args) throws NoSuchFieldException, SecurityException {

        Object anonymousClassFromStaticContext = getAnonymousClassFromStaticContext();
        Object anonymousClassFromInstanceContext = new SOExample().getAnonymousClassFromInstanceContext();

        Field[] fieldsFromAnonymousClassFromStaticContext = anonymousClassFromStaticContext.getClass().getDeclaredFields();
        Field[] fieldsFromAnonymousClassFromInstanceContext = anonymousClassFromInstanceContext.getClass().getDeclaredFields();

        System.out.println("Number of fields static context: " + fieldsFromAnonymousClassFromStaticContext.length);
        System.out.println("Number of fields instance context: " + fieldsFromAnonymousClassFromInstanceContext.length);
        System.out.println("Field from instance context: " + fieldsFromAnonymousClassFromInstanceContext[0]);

    }

}

This is the output:

Number of fields static context: 0
Number of fields instance context: 1
Field from instance context: final so.SOExample so.SOExample$2.this$0

Each method, although seemingly calling the same code, is doing something different. It looks to me that the instance method is returning a nested class, whereas the static method is returning a static nested class (as a static member, it obviously can't have a reference to this).

Given the fact that there's no reference to the enclosing class, I can't see the benefit in this.

What's going on behind the scenes?

like image 385
Robert Bain Avatar asked Jan 05 '16 21:01

Robert Bain


People also ask

What is anonymous inner class explain the types of anonymous inner classes?

It is an inner class without a name and for which only a single object is created. An anonymous inner class can be useful when making an instance of an object with certain “extras” such as overriding methods of a class or interface, without having to actually subclass a class.

Which of the following defines an anonymous inner class and also creates the instance of that class?

Explanation: D is correct. It defines an anonymous inner class instance, which also means it creates an instance of that new anonymous class at the same time. The anonymous class is an implementer of the Runnable interface, so it must override the run() method of Runnable.

Can we create instance of anonymous class?

Anonymous classes are inner classes with no name. Since they have no name, we can't use them in order to create instances of anonymous classes. As a result, we have to declare and instantiate anonymous classes in a single expression at the point of use. We may either extend an existing class or implement an interface.

Can an anonymous class have static methods?

Anonymous classes also have the same restrictions as local classes with respect to their members: You cannot declare static initializers or member interfaces in an anonymous class. An anonymous class can have static members provided that they are constant variables.


1 Answers

There is a design principle behind anonymous / inner classes: Each instance of an inner class belongs to an instance of the outer class.

Leaving out the reference to the inner class would change behavior of garbage collection: The way it's implemented, the outer class can not be garbage collected as long the inner class is alive.
This supports the idea that the inner class can not exist without the outer class.

Applications might rely on this behavior by for example by creating a temporary file and deleting it in the destructor. This way, the file will only be deleted when all inner classes are gone.

This also means that the current behavior can not be changed as changing it might break existing applications.

So you should always mark inner classes as static when you don't need the reference, because that could lead to some nice memory leaks.

Edit: Example of what I am trying to say (sorry for the terrible code quality):

class Ideone
{
    static Object[] objects = new Object[2];

    public static void main (String[] args) throws java.lang.Exception
    {
        M1();
        M2();
        System.gc();
    }

    static void M1() {
        objects[0] = new Foo().Bar();
    }
    static void M2() {
        objects[1] = new Foo().Baz();
    }
}

class Foo {
    static int i = 0;
    int j = i++;

    public Foo() {
        System.out.println("Constructed: " + j);
    }

    Object Bar() {
        return new Object() {

        };
    }
    static Object Baz() {
        return new Object() {

        };
    }

    protected void finalize() throws Throwable {
        System.out.println("Garbage collected " + j);
    }
}

Output:

Constructed: 0
Constructed: 1
Garbage collected 1

As you can see, the first Foo is not garbage collected, because there is still an "inner instance" alive. For this behavior to work, the inner class needs a reference.

Of course, it could also be implemented differently. But I would say, keeping the reference is a design decision made on purpose, so that the "inner instance" will not outlive its parent.

BTW: The Java language reference states this quite cryptically (There is no exception for inner classes which do not access the outer class):

An instance i of a direct inner class C of a class or interface O is associated with an instance of O, known as the immediately enclosing instance of i. The immediately enclosing instance of an object, if any, is determined when the object is created (§15.9.2).

like image 176
Matthias Avatar answered Nov 15 '22 15:11

Matthias