Search code examples
javaconstructorinner-classesfinalanonymous-class

Passing final variables to anonymous classes


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()

Solution

  • 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.