Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing final variables to anonymous classes

Tags:

In final variable passed to anonymous class via constructor, Jon Skeet mentioned that variables are passed to the anonymous class instance via an auto-generated constructor. Why would I not be able to see the constructor using reflection in that case:

public static void main(String... args) throws InterruptedException {
final int x = 100;
new Thread() {
    public void run() {
        System.out.println(x);      
        for (Constructor<?> cons : this.getClass()
                .getDeclaredConstructors()) {
            StringBuilder str = new StringBuilder();
            str.append("constructor : ").append(cons.getName())
                    .append("(");
            for (Class<?> param : cons.getParameterTypes()) {
                str.append(param.getSimpleName()).append(", ");
            }
            if (str.charAt(str.length() - 1) == ' ') {
                str.replace(str.length() - 2, str.length(), ")");
            } else
                str.append(')');
            System.out.println(str);
        }
    }

}.start();
Thread.sleep(2000);

}

The output is:

100
constructor : A$1()
like image 222
Ustaman Sangat Avatar asked Sep 19 '11 14:09

Ustaman Sangat


People also ask

Can anonymous class access local variable?

An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final. Like a nested class, a declaration of a type (such as a variable) in an anonymous class shadows any other declarations in the enclosing scope that have the same name.

Can anonymous classes have constructors?

3.1. Since they have no name, we can't extend them. For the same reason, anonymous classes cannot have explicitly declared constructors.


2 Answers

In this case, it's because 100 is a constant. That gets baked into your class.

If you change x to be:

final int x = args.length;

... then you'll see Test$1(int) in the output. (This is despite it not being explicitly declared. And yes, capturing more variables adds parameters to the constructor.)

like image 113
Jon Skeet Avatar answered Oct 30 '22 13:10

Jon Skeet


Here is what your program prints out on my system:

100
constructor : A$1()

So the constructor is there. However, it is parameterless. From looking at the disassembly, what happens is that the compiler figures out that it doesn't need to pass x to run() since its value is known at compile time.

If I change the code like so:

public class A {

    public static void test(final int x) throws InterruptedException {
        new Thread() {
            public void run() {
                System.out.println(x);
                for (Constructor<?> cons : this.getClass()
                        .getDeclaredConstructors()) {
                    StringBuilder str = new StringBuilder();
                    str.append("constructor : ").append(cons.getName())
                            .append("(");
                    for (Class<?> param : cons.getParameterTypes()) {
                        str.append(param.getSimpleName()).append(", ");
                    }
                    if (str.charAt(str.length() - 1) == ' ') {
                        str.replace(str.length() - 2, str.length(), ")");
                    } else
                        str.append(')');
                    System.out.println(str);
                }
            }

        }.start();
        Thread.sleep(2000);
        }

    public static void main(String[] args) throws InterruptedException {
        test(100);
    }

}

The constructor that gets generated is now:

constructor : A$1(int)

The sole argument is the value of x.

like image 35
NPE Avatar answered Oct 30 '22 13:10

NPE