Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Reassigning the outer class reference

Everyone knows (or should) that a non-static inner classe has a hidden reference to the instance of its containing class.

Let's exercise this sample class :

public class Outer {
 
    private final int i;
 
    public Outer(int i) {
        this.i = i;
    }
 
    public class Inner {
        public void print() {
            System.out.println(i);
        }
    }
 
}

Decompiling the Inner class shows that it gets a reference to an Outer instance in its constructor :

olivier$ javap -private Outer\$Inner
Compiled from "Outer.java"
public class net.thecodersbreakfast.test1.Outer$Inner extends java.lang.Object{
    final net.thecodersbreakfast.test1.Outer this$0;
    public net.thecodersbreakfast.test1.Outer$Inner(net.thecodersbreakfast.test1.Outer);
    public void print();
}

Now to the interesting part.
I was wondering if this reference could be reassigned using reflection ? Let's try out !

public class Test {
 
    public static void main(String[] args) throws Exception {
 
        Outer out1 = new Outer(0);
        Outer out2 = new Outer(42);
        Outer.Inner in = out1.new Inner();
 
        in.print(); // 0
 
        Field outerRef = in.getClass().getDeclaredField("this$0");
        outerRef.setAccessible(true);
        outerRef.set(in,out2);
 
        in.print(); // 42 !
 
    }
 
}

Heh, turns out it works.
Thanks for watching tonight's "Useless, but fun" episode !


Commentaires

1. Le dimanche 22 mai 2011, 10:13 par Piwaï

Funny enough, you can also create Inner instances with a null reference to outer class. As long as you don't use the outer reference (e.g. to call outer instance methods), everything will work fine.

This trick is actually what enables FunkyJFunctional (https://github.com/pyricau/FunkyJFu...) to work seamlessly with local classes :

class Hello System.out.println("Hello Funky World");
executorService.execute(withRun(Hello.class));

=> The withRun method creates a Runnable that will create a new Hello instance each time run() is called. The Hello instance is created with a null constructor parameter, instead of the actual outer instance.

It's quite interesting to study how local variables are transmitted to anonymous classes / local classes. I had fun with those things in the following test : https://github.com/pyricau/FunkyJFu... . I wonder if it's part of the spec, or if it may change at any time.

2. Le dimanche 26 juin 2011, 13:12 par Ilias Tsagklis

Hi Olivier,

Great blog! Is there an email address I can contact you in private?

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.