Lambda expressions in nested classes with multiple methods

Asked

Viewed 142 times

3

I was reading about Ambdas 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 ends up forcing 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

    }
});

Researching, I found that question on Soen about the doubt, but I ended up encountering 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 took a test and I really saw that it works:

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) {
        }

    }
}

To be executed:

inserir a descrição da imagem aqui

How was it possible to circumvent the requirement that the interface MouseListener imposes in this code? How does this function know that it is the method MouseClicked if not implemented it was?

Note: I am aware of the existence of Adapters, and in this case could simply use the class MouseAdapter, but the use of Mouselistener was only illustrative, as not all java listeners have an Adapter equivalent.

1 answer

5


First let’s consider the type of interface that is being used in the exemplified code, a functional interface:

Concept

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

Annotations

They usually take the note @FunctionalInterface before the interface declaration, although not mandatory.

default

The word has also been added default in these interfaces so that an interface method can have an implementation and not be abstract. This helps to get around the problem of being able to have only an abstract method, because we can implement those we don’t want abstract.

Important note

This type of interfaces are vital for a lambda expression, because lambda can only have code for one method, so for the only abstract method that is on the functional interface.

How was it possible to circumvent the requirement that the interface MouseListener imposes on that code?

In the example the functional interface MouseListenerHelper sets the implementations for the inherited interface by default, as in the given example:

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

Leaving empty and without code, but implemented. That’s why it implements 4 less 1 interface methods, the only one that was missing and that was abstract:

void mouseClicked(MouseEvent e) 

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

How does this function know that it is the method MouseClicked if nor implemented it was?

We can now play the same effect by slightly changing the interface. Removing the mouseEntered and added the 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 the mouseEntered is the only abstract method is the only one caught by the lambda:

inserir a descrição da imagem aqui

  • Functional interface??

  • 1

    Yes, that defines only an abstract method, can consult here. They are the ones that are necessary for the Ambdes, so that they know which method they implement, because it is only one.

  • @diegofm I’ve edited the answer, If you’re confused just say, I try to rearrange the text a little

  • Great answer :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.