3

With reference to this question -

How does `this` reference to an outer class escape through publishing inner class instance?

If we replace the anonymous class with the lambda or the method reference why and how this code is going to behave?

public class ThisEscape {
    public ThisEscape(EventSource source) {
         source.registerListener(e -> doSomething(e));
    }
}
Mandeep Rajpal
  • 1,667
  • 2
  • 11
  • 16

2 Answers2

5

Technically there is not much of a difference between creating an anonymous inner class or using a lambda. Lambdas are more of less just syntax candy to make something Java was capable of doing all the time more prominent, accessible and less error prone. That is your code is subject to the same race condition described in the question you referred to and its answer.

With the given code construct you got no guarantee that the instance of ThisEscape is properly created before doSomething is called.

Read the javaspecialists.eu newsletter that is linked in this answer to understand under what circumstances that might be problematic.

dpr
  • 10,591
  • 3
  • 41
  • 71
  • 3
    Lambdas are more than syntactic sugar. But of course, as long as the operation stays “register a listener that will invoke `doSomething`”, the fundamental problems remain the same, as they are intrinsic to the operation, not the way it has been implemented. – Holger Jun 29 '20 at 05:55
  • @Holger could you perhaps elaborate a little on the difference between lambdas and anonymous inner classes? I‘m not talking about the generated byte code but about the semantic differences between lambdas and AICs. I can’t think of anything that is possible with lambdas that would not be possible with AICs as well - looking awkward however. Which makes lambdas syntax candy to me. Nice candy of course... – dpr Jun 29 '20 at 09:37
  • 1
    See the 2nd to 4th paragraphs of [this answer](https://stackoverflow.com/a/47408390/2711488), especially the 3rd. – Holger Jun 29 '20 at 09:53
  • 1
    "there is no difference between creating an anonymous inner class or using a lambda" no there is, anonymous classes are just classes, while lambdas are not instances of any class... `this` is usable in a lambda but refers to the closure of the lambda itself. – Jean-Baptiste Yunès Jun 29 '20 at 16:09
4

In the original question the code

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });
    }
}

is problematic because the object have been registered in the constructor and then may be used by the event managing system while not being fully constructed. In fact, this is dangerous only if doSomething access something external to the ThisEscape instance.

And this is the same with your lambda "equivalent"

public class ThisEscape {
    public ThisEscape(EventSource source) {
         source.registerListener(e -> doSomething(e));
    }
}

But don't be fooled, anonymous inner classes are not strictly equivalent to lambdas... this refers to the current instance in case of anonymous inner class but refers to the enclosing instance of the lambda (this is in the closure of the lambda) :

interface doable {
    public void doIt();
}
public class Outer {
    public Outer() {
        doable d1 = new doable() {
            public void doIt() {
                System.out.println(this);
            }
        };
        d1.doIt();

        doable d2 = ()->System.out.println(this);
        d2.doIt();
    }
    public static void main(String []argv) {
        new Outer();
    }
}

produces something like :

Outer$1@3764951d
Outer@3cd1a2f1

The second line clearly shows that a lambda is not an instance of any class, it is not an object.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • Hi I am trying to understand this concept but unable to get it. `public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener( new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } }` in this code how the escaping this reference may cause the problem ? because if two different threads call the constructor they'll get 2 different objects. And within the constructor I cant see how there could be any problem. how can this have race condn ? – Amit Gupta Jan 29 '22 at 15:39
  • @AmitGupta No problem with two instances. The problem is that the *in construction* object is registered in an event dispatching system and then could be used by it before the constructor ends, thus the event system will deliver a event to an *not finished* object. The object is seen outside before the constructor ends... – Jean-Baptiste Yunès Feb 01 '22 at 12:51