user3780373
user3780373

Reputation:

How the below code with Lambda expressions is compiling?

I am reading a Lambda example from Herbert Shildt book- Java the complete reference. The code is written in side of LambdasAsArgumentsDemo.java file

interface StringFunc {
String func(String n);
}

class LambdasAsArgumentsDemo {

public static void main(String args[]){
    String inStr = "Lambdas add power to Java";
    String outStr;
    // Here, a simple expression lambda that uppercases a string is passed to stringOp( ).
    outStr = stringOp((str) -> str.toUpperCase(), inStr);
    System.out.println("The string in uppercase: " + outStr);

//This method has a functional interface as the type of its
// first parameter. Thus, it can be passed a reference to
//any instance of that interface, including the instance 
//created by a lambda expression.
//The second parameter specifies the string to operate on.

        static String stringOp(StringFunc sf, String s) {
            return sf.func(s);
        }
    }

Now my question is at the line

outStr = stringOp((str) -> str.toUpperCase(), inStr);

How come it is true that when you pass (str) -> str.toUpperCase() as argument, an instance of the functional interface StringFunc is created and a reference to that object is passed as the first parameter of stringOp() ?? How does it know to instantiate StringFunc ?

Upvotes: 1

Views: 71

Answers (1)

Thomas Weller
Thomas Weller

Reputation: 59208

StringFunc defines an interface for a function which takes a string as argument and returns a string. Your lambda expression (str) -> str.toUpperCase() does the same. It takes a string ((str) and returns a string (str.toUppercase()), so it matches the interface.

Think like this: there's no need to instantiate an object of that interface, since the lambda already is the instance. It's sufficient to cast it to the interface. str would then be the name of the argument.

You can achieve the same result by using a regular method with that signature:

public static void main(String args[]){
    String inStr = "Lambdas add power to Java";
    String outStr;
    outStr = stringOp(LambdasAsArgumentsDemo::mymethod, inStr);
    System.out.println("The string in uppercase: " + outStr);
}

public static String mymethod(String str) { return str.toUpperCase(); }
              ^                 ^                  ^
              |                 |                  implementation
              |                 String argument called str
              String return type

From the comments:

BTW, I have some other related question. What if there is similar 2 other functional interfaces like interface StringFunc2 { String func2(String n); } and interface StringFunc3 { String func3(int n); } In such case, how would it get resolved ? What is the explanation ? BTW, it is still compiling OK and producing the same output.

The code calls stringOp(). stringOp() takes either StringFunc, StringFunc2 or StringFunc3. So the compiler knows at compile time, which one to choose.

If, however, you define three interfaces and three functions, the code cannot compile and fails with an error message that the call is ambiguous, because the lambda could be either one.

import java.util.*;
import java.lang.*;
import java.io.*;

interface StringFunc {
    String func(String n);
}
interface StringFunc2 {
    String func(String n);
}
interface StringFunc3 {
    String func(String n);
}

class LambdasAsArgumentsDemo {

    public static void main(String args[]){
        String inStr = "Lambdas add power to Java";
        String outStr;
        outStr = stringOp((str) -> str.toUpperCase(), inStr);
        System.out.println("The string in uppercase: " + outStr);
    }

    static String stringOp(StringFunc sf, String s) {
        return sf.func(s);
    }

    static String stringOp(StringFunc2 sf, String s) {
        return sf.func(s)+"2";
    }

    static String stringOp(StringFunc3 sf, String s) {
        return sf.func(s)+"3";
    }
}

Upvotes: 2

Related Questions