Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code behaves different after converting anonymous class to lambda

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
like image 983
Dog Avatar asked Jul 15 '14 18:07

Dog


People also ask

What is difference between anonymous class and lambda expression?

Anonymous class is an inner class without a name, which means that we can declare and instantiate class at the same time. A lambda expression is a short form for writing an anonymous class. By using a lambda expression, we can declare methods without any name.

Can we replace anonymous class with Lambda?

Since the most common use of Anonymous class is to provide a throwaway, stateless implementation of abstract class and interface with a single function, those can be replaced by lambda expressions, but when you have a state field or implementing more than one interface, you cannot use lambdas to replace the anonymous ...


1 Answers

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.

like image 163
Eran Avatar answered Sep 30 '22 09:09

Eran