-1

In the below working code,

package com.ca.naive;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.List;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class TodoGUI {
    public static void main(String[] args) {
        List list = new List();
        TextField itemField = new TextField();

        Button addButton = new Button("Add");
        Button removeButton = new Button("Remove");

        addButton.addActionListener(e -> list.add(itemField.getText()));
        removeButton.addActionListener( e -> list.remove(list.getSelectedIndex()));

        Panel buttons = new Panel(new GridLayout(1,0,3,3));
        buttons.add(addButton);
        buttons.add(removeButton);

        Panel bottomPanel = new Panel(new FlowLayout(FlowLayout.RIGHT));
        bottomPanel.add(buttons);

        Panel centerPanel = new Panel(new BorderLayout());
        centerPanel.add(BorderLayout.NORTH, itemField);
        centerPanel.add(BorderLayout.SOUTH, buttons);

        Frame frame = new Frame();
        frame.setLayout( new BorderLayout() );
        frame.add(BorderLayout.WEST, list);
        frame.add(BorderLayout.CENTER, centerPanel);
        frame.pack();
        frame.addWindowListener( new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        frame.setVisible(true);
    }
}

Does javac replace addButton.addActionListener(e -> list.add(itemField.getText())); syntax with

addButton.addActionListener(new java.awt.Event.ActionListener() {
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        list.add(itemField.getText());
    }
});

?

overexchange
  • 15,768
  • 30
  • 152
  • 347
  • 1
    _[an `import` statement] ... allows you to refer to a type or a static member using a single identifier (e.g. `List`, `min`) as opposed to the fully qualified name (e.g. `java.util.List`, `Math.min`)_ You're not using a type name, so you don't need an `import` statement. – Sotirios Delimanolis Sep 25 '17 at 18:31
  • @SotiriosDelimanolis Do you mean, `javac` internally resolves type name `ActionListener` by detecting the type of object passed to `addButton.addActionListener()`? First part of your comment is understood, before this query is raised – overexchange Sep 25 '17 at 18:33
  • 1
    Yes, it knows the method you're trying to invoke expects an `ActionListener`. Presumably, that's the only applicable method. But as for the _resolve name_, there's no need to resolve a name, because you're not using a name. `import` statements only simplify source code. They do nothing else. – Sotirios Delimanolis Sep 25 '17 at 18:35
  • @SotiriosDelimanolis Yes, second part of your comment is well understood, before raising this query. Not sure, how this query is related to your referred query?As you said, `javac` does the plumbing job to resolve the type name `ActionListener`, which is the relevant part as an answer – overexchange Sep 25 '17 at 18:36
  • @SotiriosDelimanolis I'm reopening because I'm someone who has extensive familiarity with Java and the JVM, and this was surprising to me. It's not covered in the other question, and it appears there may be special scoping rules when lambda type analysis can limit the candidates. – chrylis -cautiouslyoptimistic- Sep 25 '17 at 18:57
  • @chrylis Huh? The question is _How does `ActionListener` name gets resolved without `import java.awt.event.ActionListener`?_ The answer is: it doesn't need to get resolved because they haven't used the name. Do you think the question is asking how lambda is compatible with the parameter type? Because I'm pretty certain there are dupes about functional interfaces somewhere. – Sotirios Delimanolis Sep 25 '17 at 19:01
  • 1
    @chrylis https://stackoverflow.com/questions/28180695/do-java-lambda-expressions-utilize-hidden-or-local-package-imports – Sotirios Delimanolis Sep 25 '17 at 19:06
  • @chrylis https://stackoverflow.com/questions/46144710/java-8-lambdas-and-streams-not-a-how-but-a-why – Sotirios Delimanolis Sep 25 '17 at 19:07
  • @SotiriosDelimanolis Type name `ActionListener` does get resolved, not explicitly mentioned by programmer, but `javac` does it internally, in order to appreciate lambda syntax. If type name `ActionListener` does not get resolved then `javac` cannot internally understand `new ActionListener(){...}` syntax. – overexchange Sep 25 '17 at 19:12
  • You're asking why an `import` is not necessary. An import does not do that type of resolution, ie. finding an applicable method. **If that IS what you're asking about**, please edit your question to clarify. – Sotirios Delimanolis Sep 25 '17 at 19:14
  • 3
    The question is extremely unclear to me. Lambda is not just syntactic sugar for anonymous inner classes. It will not rewrite the lambda to the anonymous inner class form, so to me it seems that your question is based on this misconception. – NickL Sep 25 '17 at 19:41
  • @SotiriosDelimanolis Okay, it looks like I misunderstood what the OP was trying to ask. It's definitely a dupe, but not of that same one. – chrylis -cautiouslyoptimistic- Sep 25 '17 at 23:36

2 Answers2

2

Does javac replace addButton.addActionListener(e -> list.add(itemField.getText())); syntax with

addButton.addActionListener(new java.awt.Event.ActionListener() {
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        list.add(itemField.getText());
    }
  });

?

No, this is not what the compiler does. In your example, an ActionListener implementation will be generated and instantiated, but that does not happen at compile time; it happens at runtime. The compiler does two things. First, it moves the body of your lambda into a hidden method that looks something like this:

void lambda$1(java.util.List list, java.awt.TextField itemField) {
    list.add(itemField.getText());
}

Second, at the point where your lambda is declared, it emits a call to a bootstrap method. The bootstrap method is a special factory method that knows how to generate an implementation of a functional interface. It needs to be given some basic information, most notably: the type of the functional interface (already known to be ActionListener); the types of any captured variables (in your case, list and itemField); and which method contains the logic for the implementation (the generated lambda$1 method).

When the bootstrap call gets hit at runtime, it will generate an ActionListener implementation. The next time you end up on this code path, you won't have to call the bootsrap method. Instead, the bootstrap call is replaced such that you end up with something equivalent to:

addButton.addActionListener(TodoGUI$Lambda$1.getInstance(list, itemField));

Where TodoGUI$Lambda$1 is a class that looks something like this:

static class TodoGUI$Lambda$1 implements java.awt.Event.ActionListener {
    private final java.util.List list;
    private final java.awt.TextField itemField;

    TodoGUI$Lambda$1(java.util.List list, java.awt.TextField itemField) {
        this.list = list;
        this.itemField = itemField;
    }

    @Override
    public void actionPerformed(java.awt.Event.ActionEvent e) {
        TodoGUI.lambda$1(list, itemField);
    }

    static java.awt.Event.ActionListener getInstance(
        java.util.List list,
        java.awt.TextField itemField) {

        return new TodoGUI$Lambda$1(list, itemField);
    }
}

Now, with all that in mind, the compiler does not need you to import the ActionListener type. That type does not need to be in the lexical scope at all. The compiler will look and see that you are calling a method called addActionListener on an java.awt.Button instance. It will see that you are passing a single argument, which is a lambda expression. In this case, there are no overloads, so it knows that addActionListener expects you to pass an ActionListener. It sees that ActionListener is a single-method interface, meaning it can be bound to a lambda. It attempts to infer your argument types and return type such that they are compatible with what is expected for an ActionListener: a single ActionEvent argument, and a void return type. Your lambda is compatible, so the call is bound, and the steps above are performed.

Mike Strobel
  • 25,075
  • 57
  • 69
0

Another "non-lambda" example of the effect you described is a call like

System.out.println("hello");

You don't need to import java.io.PrintStream in order to use the println method that comes with out being an instance of this class.

BTW: You can program a class without the use of any import statements. In that case you always have to use the full classname including all packages if you want to use it:

java.io.PrintStream outStream = System.out;
outStream.println("Hello");

An import just saves you the repeated specification of the package each time you use a class.

Lothar
  • 5,323
  • 1
  • 11
  • 27