0

In the following class:

public class ThreadTest {

    public static String name = "Member Accessed";
    
    
    public void run() {
    
        // Anonymous class
        Runnable r1 = new Runnable() { 
        
            @Override
            public void run() {
                System.out.println("Using Anonymous class, referencing this.name " + this.name);
            }
        };
        
        // lambda expression
        Runnable r2 = () -> {
            System.out.println("Using Lambda Expression, referencing this.name " + this.name);
        };
        
        new Thread(r1).start();
        new Thread(r2).start();
    }


    public static void main(String[] args) {
    
        ThreadTest tt = new ThreadTest();
        tt.run();

    }
}

Why can the lambda expression access this.name, whereas the anonymous class can not? I understand that for the anonymous class, this references the anonymous class scope, and name doesn't exist there. However, the lambda expression is basically an anonymous method that belongs to an anonymous class that implements an interface. So, technically this should also reference the anonymous class for the lambda expression.. But somehow, this manages to reference name.

Why is this the case?

Grateful
  • 9,685
  • 10
  • 45
  • 77
  • 1
    *However, the lambda expression is basically an anonymous method that belongs to an anonymous class that implements an interface* No. It isn't. Lambda expressions use the new bytecode mechanism `invokedynamic` and [`LambdaMetafactory`](https://github.com/openjdk/jdk/blob/a445b66e58a30577dee29cacb636d4c14f0574a2/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java); there is no anonymous class in a lambda. – Elliott Frisch Mar 01 '23 at 04:42
  • @ElliottFrisch I know that's what is going on at the compiler level... But lambdas are anonymous methods. You can't deny that. But then if this anonymous method doesn't belong to an anonymous class, then how do you explain the fact that it implements an interface? I can understand a class being able to implement an interface... But How on earth does a method implement an interface directly? Please explain this. – Grateful Mar 01 '23 at 04:48
  • See [What are functional interfaces used for in Java 8?](https://stackoverflow.com/questions/36881826/what-are-functional-interfaces-used-for-in-java-8) – Elliott Frisch Mar 01 '23 at 04:51
  • @ElliottFrisch Again, your reply is much appreciated... But would kindly be more specific? Which part do you want me to focus on? I had a look at the link that you provided, but it doesn't help explain how lambda expressions help implement interfaces without an anonymous class. Please help me elaborate on this. Thanks in advance! – Grateful Mar 01 '23 at 04:55
  • Basic Java fundamentals state that interfaces can NOT be instantiated. So, I do not understand how functional interfaces can be implemented and in essence, instantiated (using `new` keyword) without the use of intermediary anonymous classes. Please explain this. Thanks . – Grateful Mar 01 '23 at 05:00
  • Because they changed Java to allow interfaces that have a single abstract method to be instantiated when they are used in a lambda. They did this quite some time ago now. The exact way they did it was adding the `invokedynamic` instruction to the JVM and implementing `LambdaMetafactory`. You can see this with `javap -v`. – Elliott Frisch Mar 01 '23 at 05:04
  • @ElliottFrisch Aaah, okay! But do you have any official documentation that states what you've just said? Also, given what you've said, it now seems that those sites that claim lambdas to be anonymous classes... Are wrong? In essence, lambdas are just anonymous functions? But additionally, they can be used to provide an implementation for functional interfaces and help instantiate them? Sound right to you? – Grateful Mar 01 '23 at 05:20
  • 1
    [JEPS-126](https://openjdk.org/jeps/126) says (in part) *The currently preferred implementation approach for lambda expressions relies on `invokedynamic` and method handles introduced by JSR 292.* You might also notice that interfaces are now allowed to have `default` implementations. Lots of changes in Java these days. – Elliott Frisch Mar 01 '23 at 05:38

1 Answers1

0

First of all, fix this as long as you refer to the instance and not the class variable:

public String name = "Member Accessed";

Though each lambda expression can be expressed as an anonymous class, their bytecodes differ, as Eliott Frisch said. I am not knowledgeable enough to provide more details, but there is a very interesting reading from Brian Goetz I can refer you to. Sadly, the script is accessible only through this answer as long as the original link is no longer active: Lambda Translation Strategy.


Anyway, the anonymous class can point out the instance variable of the outer class by specifying the class name using this.

Runnable r1 = new Runnable() {

    @Override
    public void run() {
        System.out.println("Using Anonymous class: " + ThreadTest.this.name);
    }
};
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Thank you. I know you can refer to `ThreadTest.this.name` from the anonymous Runnable class. But if accordingly to you lambdas are anonymous classes (and not anonymous methods) then why does `this.name` work directly from a lambda expression which should have it's own scope because of it being an anonymous class. – Grateful Mar 01 '23 at 05:25
  • I said they can be "expressed" as anonymous classes, but not that they are equivalent, otherwise, their bytecode would be also the same, and referencing through `this.name` would not work. I am also taking some time to grasp this :) – Nikolas Charalambidis Mar 01 '23 at 05:27
  • Thank you for your honesty. In my opinion, if lambdas are not anonymous classes, we should avoid using words like "lambdas can be expressed as ...". I think the official oracle documentation explicitly mentions that lambdas are anonymous functions... So let's just stick to that. – Grateful Mar 01 '23 at 05:33
  • "express as" does not really mean "equal to", but I got your point. There is nowhere in the specification that lambda expression is an anonymous class (that's why I never write in my answers that lambda expression "is" anonymous class): https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27 Though, there might appear on some Oracle website that lambda expression "is" anonymous class, I haven't found such a page. I'd definitely choose not to stick that they are equal. – Nikolas Charalambidis Mar 01 '23 at 05:40