Java 8 : Lambda Expressions part 2

What is the Type of Lambda Expression?

Java is a strongly typed language. For a lambda expression assigned to a variable, it needs to be of proper type. The type of the lambda expression is the Functional interface. Target type can be understood in 2 context – One , when the lambda expression is assigned to a variable and second when the lambda expression is passed to a method. Both cases, compiler check whether the lambda expression matches with the functional interface. Let us see this by an example.


public class LambdaTargetType {

interface Greetings{
String greet(String name);
}

public void testGreet(String name, Greetings greetImpl){

String result =greetImpl.greet(name);
System.out.println(result);

}

}

So above we have an interface Greetings that has only one method, hence its a functional interface. We have a testGreet method to test this interface implementations. A lambda expression for this interface may look like this:

(String s)->”Hello, “+s

What if we want to assign this to a variable.

Greetings greetImpl1=(String s)->”Hello, “+s;

The compiler will look at the type Greetings and then look into the method signature of that functional interface. The single input we passed in the lambda expression matches the method input parameter. Now compiler starts inspecting the return value of the method and the lambda expression, which are same here. It then check for any exceptions thrown which is not performed here.


public static void main(String[] args) {
LambdaTargetType aLambdaTargetType=new LambdaTargetType();
Greetings greetImpl1=(String s)->"Hello, "+s;
Greetings greetImpl2=(String s)->"Hi, "+s;
aLambdaTargetType.testGreet("Sam", greetImpl1);
aLambdaTargetType.testGreet("Sam", greetImpl2);
aLambdaTargetType.testGreet("Sam", (String s)->"Greetings!! , "+s);
}

Output:


Hello, Sam
Hi, Sam
Greetings!! , Sam

Similar check is performed if we pass a lambda expression directly to a method.

Capturing Lambda

Till now we have not used any parameters apart from the parameters that are passed to the lambda. Lets see an example where we use a parameter not present in the lambda. These types of lambda expressions which uses the parameters from the enclosing scope is called capturing lambda.


public class CapturingLambda {

interface Greetings{
String greet(String name);
}

public void testGreet(String name,String language){
Greetings greetImpl=(String s)->{
if(language.equalsIgnoreCase("es")){
return "Hola, "+s;
}else{
return "Hello, "+s;
}
};
String result =greetImpl.greet(name);
System.out.println(result);
}

public static void main(String[] args) {
CapturingLambda aCapturingLambda=new CapturingLambda();
aCapturingLambda.testGreet("Sam", "es");
aCapturingLambda.testGreet("Sam", "en");
}

}

Output:


Hola, Sam
Hello, Sam

Above we are using both the lambda parameter and the parameters from the enclosing scope in the method arguments. Notice that the parameter ‘language’ are not declared as final variables. We know that when we are working with anonymous classes, any enclosing methods parameters that are used in the anonymous classes must be declared final. This restriction is now relaxed. Although you don’t have to put the final modifier before them , they are expected to be effectively final. So  if you try to reassign something to the variable you will get errors in your lambda expressions.

CaptureLambdaError1

We know that local variables exists on stack memory as opposed to HEAP memory for class or instance variables and hence they are treated as thread safe as no other thread can have access to this stack. In case of local variables the compiler will assist us and will not allow us to change the local variables. However this restriction does not apply to the class or instance variables.  It is the developer responsibility to make sure that the instance variables are not to be mutated in a threaded environment inside the lambda expression.

Lambda Scoping

Lambda expression scope is going to be exactly same as that of the enclosing method where the lambda has been created.


public class LambdaParent {
final String var="LambdaParent";
}


public class LambdaChild extends LambdaParent{

final String var="LambdaChild";

interface Family{
void display(String member);
}
public void testScope(String var){
Family aFamily=(fVar)->{
System.out.println("Inner Local="+fVar);
System.out.println("Outer Local="+var);
System.out.println("This="+this.var);
System.out.println("Super="+super.var);
};
aFamily.display(var);
}
public static void main(String[] args) {
LambdaChild aLambdaChild=new LambdaChild();
aLambdaChild.testScope("Lambda");
}
}

Output:


Inner Local=Lambda
Outer Local=Lambda
This=LambdaChild
Super=LambdaParent

So, this/super will have the same meaning.

Advertisements
%d bloggers like this: