13

I'm trying to refactor this code to use a lambda instead of anonymous class. It's a simple list of items in a GUI. I register a different listener to each item, and the last item created does something special when clicked.

class ItemList {
    interface OnClickListener {
        void onClick();
    }
    OnClickListener current;
    OnClickListener newListener(final int n) {
        return current = new OnClickListener() {
            public void onClick() {
                if (this == current)
                    System.out.println("doing something special (item #"+n+")");
                System.out.println("selected item #" + n);
            }
        };
    }
    public static void main(String[] args) {
        ItemList l = new ItemList();
        OnClickListener ocl1 = l.newListener(1);
        OnClickListener ocl2 = l.newListener(2);
        OnClickListener ocl3 = l.newListener(3);
        ocl1.onClick();
        ocl2.onClick();
        ocl3.onClick();
    }
}

It works as expected:

$ javac ItemList.java && java ItemList
selected item #1
selected item #2
doing something special (item #3)
selected item #3

Now I change it to use a lambda instead of anonymous class:

class ItemList {
    interface OnClickListener {
        void onClick();
    }
    OnClickListener current;
    OnClickListener newListener(final int n) {
        return current = () -> {
            if (this == current)
                System.out.println("doing something special (item #"+n+")");
            System.out.println("selected item #" + n);
        };
    }
    public static void main(String[] args) {
        ItemList l = new ItemList();
        OnClickListener ocl1 = l.newListener(1);
        OnClickListener ocl2 = l.newListener(2);
        OnClickListener ocl3 = l.newListener(3);
        ocl1.onClick();
        ocl2.onClick();
        ocl3.onClick();
    }
}

But now it no longer does anything special on the last item? Why? Does == work differently with lambdas? I thought it was a bug in retrolambda at first, but this example is running on plain JDK8 and it still happens.

$ javac A.java && java A
selected item #1
selected item #2
selected item #3
1
  • 1
    Lambdas can't really refer to this in their body. Commented Jul 15, 2014 at 18:09

1 Answer 1

13

this inside lambda doesn't mean the same as this inside an anonymous class instance.

Inside a lambda it refers to the enclosing class.

...the lambda expression does not introduce a new level of scoping. Consequently, you can directly access fields, methods, and local variables of the enclosing scope. ... To access variables in the enclosing class, use the keyword this. ...

Inside an instance of an anonymous class, it refers to the current object

Within an instance method or a constructor, this is a reference to the current object — the object whose method or constructor is being called

That's why in the lambda expression, this == current is never true, since it compares an instance of class ItemList with a lambda expression of type OnClickListener.

Sign up to request clarification or add additional context in comments.

9 Comments

Still it's not clear why current==this is never true. Since enclosing class is the same, we should see three "something" in the output.
@OlegGryb The enclosing class is ItemList, so it's never equal to current (which is the lambda expression of type OnClickListener).
You're right, didn't think it through, so current.onClick() should still work if I want to use it this way.
@Dog and Eran, no, you can't compare any two objects with ==; try comparing two instances of unrelated concrete classes for example. But you can generally compare two references if at least one of them is an interface, even if one of them is this. The reason is that the class reference might be an instance of a subclass that happens to implement the interface type of the reference being compared.
@Eran I should say that I was also initially confused by why this wasn't a compile-time error, and it took some digging and some thinking to understand why it wasn't. It's quite subtle.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.