Anonymous Classes
in Java are inner classes
without a name, these are used in Java for override
the method without actually creating a subclass. Anonymous Inner Class
can use the local variable of other classes.
package org.wesome.java8;
abstract class Fuji {
abstract void loveApple();
}
class Apple {
public static void main(String[] args) {
String appleName = "Fuji";
Fuji fuji = new Fuji() {
@Override
void loveApple() {
System.out.println("i love " + appleName + " apple.");
}
};
fuji.loveApple();
}
}
Anonymous Inner Class
can use the local variable
of the outer class but it cannot modify
them. the below code will result in Compile Time Exception
.
package org.wesome.java8;
abstract class Fuji {
abstract void loveApple();
}
class Apple {
public static void main(String[] args) {
String appleName = "Fuji";
Fuji fuji = new Fuji() {
@Override
void loveApple() {
appleName = appleName.toUpperCase();
System.out.println("i love " + appleName + " apple.");
}
};
fuji.loveApple();
}
}
Variable 'appleName' is accessed from within the inner class, needs to be final or effectively final
The Lambda Expression
was introduced by Java to reduce the complexity of the Anonymous Inner Class
, but it carries the same functionality as the Anonymous Inner Class
, the Lambda Expression
can access but cannot modify the Local Variables
.
package org.wesome.java8;
import java.util.stream.IntStream;
class Apple {
public static void main(String[] args) {
int temp = 1;
IntStream.rangeClosed(1, 5).forEach(i -> {
System.out.println("value of temp is = " + temp);
});
}
}
The below code trying to update the temp variable in the lambda expression will result in a compilation error.
package org.wesome.java8;
import java.util.stream.IntStream;
class Apple {
public static void main(String[] args) {
int temp;
IntStream.rangeClosed(1, 5).forEach(i -> {
temp = i;
System.out.println("value of i is = " + i);
});
}
}
Variable used in lambda expression should be final or effectively final
Since Lambda Expression
is using the Local variable
, it cannot be modified outside the Lambda Expression
as well.
package org.wesome.java8;
import java.util.stream.IntStream;
class Apple {
public static void main(String[] args) {
int temp = 1;
IntStream.rangeClosed(1, 5).forEach(i -> {
System.out.println("value of temp is = " + temp);
});
temp = 2;
}
}
local variables referenced from a lambda expression must be final or effectively final
Java Language Specification
answers the reason for the above exception
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
a
What is Final and Effective Final Variable
Final Variable
: a variable that is declared with the final keyword cannot be modified and will have the same value throughout the application.
Effective Final Variable
: a variable that is not explicitly declared with a final keyword but never gets modified throughout the application, although it's not declared as final since the value has not been modified hence compiler treats them just like final and can be termed as Effective Final.
If the Local Variable is just used but never gets updated inside the lambda expression hence no need to declare it as final.
so the question arises, why does Lambda Expression
need a variable to be a final
or effective final
The restriction to effectively final variables prohibits access to dynamically changing local variables, whose capture would likely introduce concurrency problems.
Concurrency
Streams
allow Lambda Functions
to execute in sequential
and parallel
as well. if the Lambda Expression
will update the local variable
defined outside, it might result in an incorrect value
. local values are saved in stack
, each thread
has its own call stack
, and no thread can access other thread stack
package org.wesome.java8;
import java.util.stream.IntStream;
class Apple {
public static void main(String[] args) {
int temp = 1;
IntStream.rangeClosed(1, 5).parallel().forEach(i -> {
temp = i;
});
System.out.println("value of temp variable is = " + temp);
}
}
Why Lambda Requires Final or Effective Final
The main reason behind the requirement of final
and Effective Final
is Lambda Expressions
can use local variables defined outside. compiler
passes the variable in lambda using pass by value
, lambda tries to keep the value as constant
because changing the value of a variable with each iteration might result in an incorrect result
.
The process of keeping local variables is known as capturing lambdas
.
Lambda
can capture static
variables, instance
variables, and local
variables, but only local
variables must be final
or effectively final
.
local variables
are saved in the stack
, each thread
has its own call stack
, and no thread
can access another thread stack
, where are static
and instance
variables are stored on the heap
which is shared among all threads.
Then Why Lambda Allows local variable update if declared as one element array
Lambda expression
doesn't allow updating of local variable
if it's not declared as final
but it does allow to update local variable
if it's declared as one element array
.
package org.wesome.java8;
import java.util.Arrays;
import java.util.stream.IntStream;
class Apple {
public static void main(String[] args) {
final int[] temp = new int[1];
IntStream.rangeClosed(1, 5).parallel().forEach(i -> {
temp[0] = i;
});
System.out.println("value of staticTemp variable is = " + Arrays.toString(temp));
}
}
To understand this, we first have to understand how the final
works.
When a variable is declared as final, it means its reference to the object is set to final.
final String apple = "Fuji";
int this case, reference apple has been updated with the memory location of string Fuji and will never be allowed to be updated, but
final String[] apple = {"Fuji"};
The array reference variable apple has to be updated with the memory location of the start of the array, but the array contains the address of the element Fuji, which is not declared final and hence can be changed.
So the array reference variable having memory location of Array is constant and hence cannot be updated, but values inside the array can be updated.
Instance and static Variable in Lambda Expression
Java allows the instance variable and static variables to be used and modified as well inside the lambda expression.
package org.wesome.java8;
import java.util.stream.IntStream;
class Apple {
static int staticTemp;
private int instanceTemp;
public static void main(String[] args) {
Apple apple = new Apple();
IntStream.rangeClosed(1, 5).forEach(i -> {
apple.instanceTemp = i;
staticTemp = i;
});
System.out.println("value of instanceTemp variable is = " + apple.instanceTemp);
System.out.println("value of staticTemp variable is = " + apple.staticTemp);
}
}