Lambda expressions in nested classes with multiple methods

3

I was reading about lambdas and out of curiosity, I wanted to know why it is not allowed to use when the class / interface has more than one method, which forces us to do things like below:

component.addMouseListener(new MouseListener() {

    @Override
    public void mouseReleased(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // TODO Auto-generated method stub

    }
});

Searching, I found this question on SOEn about the doubt, but I ended up coming across a solution that circumvents this restriction , as can be seen in this answer :

// note the absence of mouseClicked…
interface ClickedListener extends MouseListener
{
    @Override
    public default void mouseEntered(MouseEvent e) {}

    @Override
    public default void mouseExited(MouseEvent e) {}

    @Override
    public default void mousePressed(MouseEvent e) {}

    @Override
    public default void mouseReleased(MouseEvent e) {}
}
     

You need to define such a helper interface only once.

     

Now you can add a listener for click-events on a Component c like   this:

c.addMouseListener((ClickedListener)(e)->System.out.println("Clicked!"));

I ran a test and I actually saw it work:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class MouseListenerLambdaTest {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new MouseListenerLambdaTest();
        });
    }

    public MouseListenerLambdaTest() {
        initComponents();
    }

    private void initComponents() {

        JFrame f = new JFrame();
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setPreferredSize(new Dimension(200, 120));

        JPanel contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        f.setContentPane(contentPane);
        contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        JLabel label = new JLabel();
        contentPane.add(label);

        contentPane.addMouseListener((MouseListenerHelper) (e) -> label.setText("Clicked"));

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    interface MouseListenerHelper extends MouseListener {

        @Override
        public default void mouseEntered(MouseEvent e) {
        }

        @Override
        public default void mouseExited(MouseEvent e) {
        }

        @Override
        public default void mousePressed(MouseEvent e) {
        }

        @Override
        public default void mouseReleased(MouseEvent e) {
        }

    }
}

Running:

Howwasitpossibletocircumventthe MouseListener / a> enforces this code? How does this function know that it is the MouseClicked method if it is not implemented?

  

Note: I am aware of the existence of adapters, and in this case I could simply use the MouseAdapter class, but the use of MouseListener was only illustrative, as not all java listeners have an equivalent adapter. p>

    
asked by anonymous 12.07.2017 / 15:13

1 answer

5

First consider the type of interface that is being used in the sample code, a functional interface:

Concept

Functional interfaces ( Functional Interface ) were introduced in Java 8 to support the lambda expressions. These are different from the normal interfaces because they only have an abstract method.

Annotations

Typically, they take the @FunctionalInterface annotation before the interface declaration, although it is not mandatory.

default

The word default has also been added in these interfaces so that an interface method can have an implementation and not be abstract. This helps to work around the problem of having only one abstract method, since we can implement the ones we do not want abstract.

Important Note

This type of interface is vital for a lambda expression, since lambda can only have code for a method, so for the only abstract method that is in the functional interface.

  

How was it possible to circumvent the requirement that the interface    MouseListener enforces this code?

In the example, the interface MouseListenerHelper defines the implementations for the interface inherited through default, as in the given example:

@Override
public default void mouseEntered(MouseEvent e) {
}

Leaving empty and no code, but implemented. So it implements 4 methods of the interface minus 1, the only one that was missing and that was abstract:

void mouseClicked(MouseEvent e) 

That is exactly what is caught in the lambda because it can only have an abstract method, and simultaneously it answers the question:

  

As this function learns that it is the MouseClicked method,   nor implemented it was?

We can now reproduce the same effect by slightly changing the interface. Removing mouseEntered and added mouseClicked :

@FunctionalInterface
interface MouseListenerHelper extends MouseListener {

    @Override
    public default void mouseExited(MouseEvent e) {
    }

    @Override
    public default void mousePressed(MouseEvent e) {
    }


    @Override
    public default void mouseReleased(MouseEvent e) {
    }

    //agora com mouse clicked em vez de mouse entered
    @Override 
    public default void mouseClicked(MouseEvent e) {
    }

}

Now as mouseEntered is the only abstract method is the only one caught by lambda:

    
12.07.2017 / 16:15