- Introduction
- Functional Interface
- java.util.function package
- Lambda Expression
- Variable scope
- Anonymous class
- Method reference types
- Quiz
- Conclusion
This article is meant to build a common language with the reader which will be used to answer the following questions: What are free variables? Are we allowed to change them inside the lambda expression? Is it considered dangerous to mutate them?
Prerequirements
It is highly recommended that you will control Java Generics.
Acknowledgements
The people listed below have provided feedback and edit which has helped improve the quality of this article.
@damirbar - Special thanks to Damir Bar for acting as the provisional grammar consultant.
Definition 1.1. Functional Interfaces are interfaces containing single abstract method and any number of default/static methods.
The compiler will define those interfaces as functional Interfaces. Also, we can add particular annotation above the interface, so the compiler will warn us if we declare more then one abstract function inside the interface.
Also, the Javadoc will explicitly mention that this interface is a Functional interface.
The compiler permits us to not explicitly add the @FunctionalInterface
annotation to an interface with one abstract method so the interface will be functional Interfaces, it is optional and recommended.
@FunctionalInterface
interface MyInterface
{
void f1();
}
An important note about exceptions - In a case that the method which overrides f1
can throw exceptions, the declaration of f1
inside the functional Interface must consist of the possible exceptions which f1
may throw:
@FunctionalInterface
interface MyInterface
{
void f1() throws Exception, DataFormatException, IOException;
}
The following section contains common functional interfaces.
The most common functional interfaces defined in java.util.function
package. More specific functional interfaces defined in other packages.
Interface::Method | Parameters Type | Return Type | More Info |
---|---|---|---|
Function<T,R>::apply |
T |
R |
|
Consumer<T>::accept |
T |
void |
Expected to operate via side-effects |
Predicate<T>::test |
T |
boolean |
|
Supplier<T>::get |
None | T |
|
BiFunction<T,U,R>::apply |
T ,U |
R |
|
UnaryOperator<T> implements Function<T,T> |
T |
T |
|
BinaryOperator<T> implements BiFunction<T,T,T> |
T ,T |
T |
|
ToIntFunction<T>::applyAsInt |
T |
int |
|
DoubleConsumer::accept |
double |
void |
Expected to operate via side-effects |
ObjIntConsumer<T>::apply |
Object ,int |
void |
Expected to operate via side-effects |
Note: Do not create your own functional interfaces., instead use what Java provides in java.util.function
package or any other packages. You should also read and memorize the use case of each functional interface. For example, by using Predicate
interface, you should only check if an input parameter is fulfilled in some conditions. You should not do any side effects in the process because the caller will assume you are using this interface as java recommended you to use it.
Understanding side-effects
is not mandatory to get the idea and use lambdas.
Definition 1.2. A function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value.
An essential concept in functional programming is also pure functions.
Definition 1.3. A function may be considered a pure function if both of the following statements about the function hold:
The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while a program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices.
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
Java 8 introduced a new feature which takes us one step toward functional programming. It is the ability to create functions at runtime with lambda expressions.
Definition 2.1. A variable which wasn't defined as final, but never changed is also effectively final.
Definition 2.2. By referring free variables of a function, we refer to all the variables that weren't define inside the function nor parameters of that function.
Definition 2.3. Lambda Expression is the method implementation of a functional interface. It has zero or more parameters and a body that will (or not) execute at a later stage. Inside the body, we have access to its free variables.
Function<Integer,Integer> lambda1=(Integer a) -> a+1 ;
Let's break it down:
Function
is the Functional Interface that the lambda is implementing.- The
Function<Integer, Integer>
interface contains, as expected, a single method which accepts an Integer as a parameter and returns Integer. lambda1
is the name of the object from a class implementingFunction<Integer, Integer>
interface.(.....)
contains all the parameters of the lambda. The number of parameters and their types must correspond to the declaration of the instance method insideFunction
.->
separates the parameters and the body of the lambda.a+1
is the body of the lambda. It returns the value ofa+1
. In case the body has more than one line, use{
,}
:
int x=10;
Function<Integer,Integer> lambda2=(Integer a) -> {
a++;
return a+x;
}
To invoke the lambda:
int y=lambda2.applay(10);
y
will be initilize with the value 21
.
Variable scope | Read | Write | More Info |
---|---|---|---|
Lambda expression | T |
T |
|
Enclosing method | T |
F |
Must be final or effectivly final |
Enclosing class | T |
T |
Why can't we write to a variable created at the enclosing method?
-
To avoid additional synchronizations while running the enclosing method and the lambda body in parallel.
-
The lifetime of a variable created at the enclosing method may have a shorter life than the lambda itself. In other words, the lambda may be invoked after the enclosing method returned. It means we will write to a variable that may not exist.
We conclude that the lambda must keep somehow a copy of all the variables it uses in the enclosing method. The current implementation of Java may add all the variables (in the enclosing method or class) the lambda read or write to the list of parameters the lambda get. It may also add only the variables from the enclosing method to the list of parameters because the rest can be reached in other ways. The lambda it private static method or private instance method inside the enclosing class. The decision of making it static method or instance method is decided by (can be changed in the future): If the lambda does not use any variable of the enclosing class, it may become a static method. Else it may become an instance method.
For example:
public class A {
public static void main(String[] args) {
Consumer<Integer> f2 = (Integer x) -> System.out.print(x);
}
}
After complining:
javac A.java
javap -p A
Output:
Compiled from "A.java"
public class A {
public A();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.Integer);
}
Another example:
public class A {
int y=9;
public void f1() {
Consumer<Integer> f2 = (Integer x) -> System.out.print(y);
}
}
After complining:
javac A.java
javap -p A
Output:
Compiled from "A.java"
public class A {
int y;
public A();
public void f1();
private void lambda$f1$0(java.lang.Integer);
}
It is a common mistake that lambdas translated to objects of anonymous classes that implement the functional interface. When creating an object of an anonymous class, first a class file is generated and then a new object is initialized. Creation of a file for each object of an anonymous class is a huge overhead to the VM in runtime when lambdas are widely used in every application.
When your lambda has one line, calls a method with all the parameters of the lambda and return what that function returns then you better use method reference.
The following line
Consumer<Integer> lambda1 = (Integer a) -> System.Out.println(a);
is recoomended to be written as:
Consumer<Integer> lambda1 = System.Out::println;
The parameter of the lambda is used for calling to the method println
without us needing to write them explicitly.
There are 4 types of Method references: Object::instanceMethod, Class::staticMethod, class::InstanceMethod and class::contructor.
Object::instanceMethod
as the name implies, the lambda calls a method of an object. The first example applies here too:
Consumer<Integer> lambda1 = System.Out::println;
lambda1.accept("hi!!"); // will print: hi!!
Class::staticMethod
public class App {
private static int f1() {
return 10;
}
public static void main( String[] args )
{
Supplier<Integer> lambda1=App::f1; // () -> App.f1();
int x=lambda1.get(); // x = 10
}
}
class::InstanceMethod
The first parameter will invoke one of his methods and use the second parameter as an argument:
public class App {
public int f1() {
return 8;
}
public static void main( String[] args ) throws IOException
{
Function<App,Integer> lambda1 = App::f1; // (App a)-> a.f1();
int x = lambda1.applay(new App()); // x = 8
}
}
public class App {
public int f2(App a) {
return 16;
}
public static void main( String[] args ) throws IOException {
BiFunction<App,App,Integer> lambda1 = App::f2; // (App a,App b) -> a.f2(b);
int y = lambda1.applay(new App()); // y = 16
}
}
class::contructor
Function<Integer,int[]> lambda1 = int[]::new; // (int x)-> new int[x];
int[] array = lambda1.apply(10);
class A
{
public static void main(String[] args)
{
Supplier<A> lambda1 = A::new; // (int x)-> new A();
A a1 = lambda1.get();
}
}
Note: It is possible to use super
/ this
as an object inside methods:
class A
{
void f1(int x)
{
System.out.println("father is printing!");
}
}
class B extends A
{
B()
{
Consumer<Integer> lambda1 = super::f1; // (int x) -> super.f1(x);
lambda1.accept(10); // print: father is printing!
}
void f1(int x)
{
System.out.println("son is printing!");
}
}
The Answers are at the end of the quiz.
Search for Runnable
funcation interface's method signature. Could we write inside the following code: Runnable runnable1 = this::f1;
? Name a readability disadvantage while sending this::f1
as a parameter to a method.
// Class MyClass1
public void f2() {
Runnable runnable1 = () -> {
System.out.println("hi");
};
}
public void f1() {
System.out.println("hi");
}
What are the differences between the following functional interfaces: Runnable
, Callable
and Comparable
? Write lambdas using those functional interfaces and test them out.
What are the differences between side effects and pure function?
What are the four main new funcational interfaces in Java 8
? Which of them can be implemented with side effects or pure function?
What does the following code prints?
Predicate<Integer> p1 = (Integer x) -> x % 2 == 0;
Predicate<Integer> p2 = (Integer x) -> x % 3 == 0;
System.out.println(p1.or(p2).test(1));
System.out.println(p1.and(p2).test(2));
System.out.println(p1.and(p2).and(p1).test(3));
System.out.println(p1.negate().test(4));
Fill the types which are missing (Type1
,Type2
,Type3
):
Function<Type1,Type2> f1=(Integer x)->"hi"+x;
f1.andThen((Type3 something)->something.length());
What does this code prints?
What does this code prints?
Function<String, Integer> f1 = (String x) -> x.length();
Integer[] x = f1.andThen((Integer number) -> new Integer[number])
.apply("123");
In the following questions, you need to determine if there is any compilation error, undefined behavior or there is nothing wrong. They are confusing and tricky. Take your time and explain your answer with as many details as possible.
Assume all the threads don't fail to run.
static int count=1;
public static void f1() {
for(int i=1;i<=10;i++)
new Thread(()->count++).start();
}
Assume all the threads don't fail to run.
public static void f2() {
for(int i=1;i<=10;i++)
new Thread(()->System.out.println(i)).start();
}
Search for Runnable
funcation interface's method signature. Could we write inside the following code: Runnable runnable1 = this::f1;
? Name a readability disadvantage while sending this::f1
as a parameter to a method.
// Class MyClass1
public void f2() {
Runnable runnable1 = () -> {
System.out.println("hi");
};
}
public void f1() {
System.out.println("hi");
}
Yes, we could. The implementation of Runnable
function interface is:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
To understand the disadvantage, let us examine the following code:
class Application{
static void doSomeWork() {}
static void f2(Runnable r1) {
r1.run();
}
static void doSomeWork(String str) {}
static void f3(Consumer<String> r1) {
r1.accept("333333");
}
public static void main(String[] args) {
// part A
f2(() -> doSomeWork()); // line 1
f2(Application::doSomeWork); // line 2
// Part B
f3((String str) -> doSomeWork(str)); // line 3
f3(Application::doSomeWork); // line 4
}
}
In line 1
we are sending to f2
a lambda which calls doSomeWork
with no params. We explicitly see that we dont send any params and doSomeWork
does not get any params. in line 2
we can't explicitly see it; in line 2
we must go to the signature of doSomeWork
(which we will find 2 of them) and understand if doSomeWork
get params or not. The only way to understand which overload of doSomeWork
we need to look into is by looking at the signature of f2
and see that it gets Runnable
which means that the overload of doSomeWork
we are searching for, doesn't get any params.
We have the same problem of readability in line 4
. It means that by the only looking with our eyes at lines 2
and 4
, we can't understand what the signature of doSomeWork
is and we can't understand that there are two overloads to doSomeWork
method. But by looking at lines 1
and 3
, we can easily see that there are two overloads to the method doSomeWork
.
What are the differences between the following functional interfaces: Runnable
, Callable
and Comparable
? Write lambdas using those functional interfaces and test them out.
The functional interfaces' method signatures:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
// it is optional to specify @FunctionalInterface here.
public interface Comparable<T> {
public int compareTo(T o);
}
They all have a different signature, so they not the same. Also, by the implementation details(java docs) we can see that they are intended for different use cases. The only way to know them is to read them. Misusing them will lead to bugs between different developers.
What are the differences between side effects and pure function?
The definitions from Wikipedia are:
Definition 1.2. A function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value.
Definition 1.3. A function may be considered a pure function if both of the following statements about the function hold:
The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while the execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices.
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
What are the four main new funcational interfaces in Java 8
? Which of them can be implemented with side effects or pure function?
The four main new functional interfaces in Java 8
are: Consumer
, Supplier
, Function
and Predicate
.
Consumer
- Get an object and do something with it. We are allowed to use side effects here.Supplier
- (from the java docs) Represents a supplier of results. There is no requirement that a new or distinct result be returned each time the supplier is invoked.
It means we implement a Supplier
not as a pure function.
-
Function
- Get an object from typeA
and return object from typeB
(TypeA
can be equal to typeB
). No side effects are allowed here. But sometimes we will implement it as a transformer from id to an object we retrieved from a server. A server call has side effects. -
Predicate
- Get an Object a determine if it satisfies a condition. No side effects are allowed here. No side effects are permitted here.
What does the following code prints?
Predicate<Integer> p1 = (Integer x) -> x % 2 == 0;
Predicate<Integer> p2 = (Integer x) -> x % 3 == 0;
System.out.println(p1.or(p2).test(1));
System.out.println(p1.and(p2).test(2));
System.out.println(p1.and(p2).and(p1).test(3));
System.out.println(p1.negate().test(4));
It will print:
false
false
false // In this case, there is no effect to the extra `and(p1)`.
false
Fill the types which are missing (Type1
,Type2
,Type3
):
Function<Type1,Type2> f1=(Integer x)->"hi"+x;
f1.andThen((Type3 something)->something.length());
What does this code prints?
Solution:
Function<Integer,String> f1=(Integer x)->"hi"+x;
f1.andThen((String something)->something.length());
The code doesn't print anything. We use f1
as it was a normal object, which means we need to call one of it's methods to invoke something. In this case, we need to write f1.apply(some value)
.
What does this code prints?
Function<String, Integer> f1 = (String x) -> x.length();
Integer[] x = f1.andThen((Integer number) -> new Integer[number])
.apply("123");
This code prints nothing because we didn't send anything to the screen. But the call to apply("123")
will return integer[]{0,0,0}
.
Assume all the threads don't fail to run.
static int count=1;
public static void f1() {
for(int i=1;i<=10;i++)
new Thread(()->count++).start();
}
Answer: undefined.
count
is a static property so as a result, he will never be effectivly final. Also, we can read and write to a static variable from the lambda's body. Due to the absence of synchronization on count
, we conclude that the result is undefined.
Assume all the threads don't fail to run.
public static void f2() {
for(int i=1;i<=10;i++)
new Thread(()->System.out.println(i)).start();
}
Answer: Compilation error.
i
is a free variable and also is a local variable of the enclosing function. i
will be mutated after its initialization so it can't be used inside the lambda. It must be defined as defined as final or effectively final. So the lambda can read it's value.
The big difference between method reference and lambda expression is method reference references to the method directly it should invoke. The lambda is responsible for invoking a method (in case there is a call to a method inside the lambda body). It means we called two methods instead of one.
© Stav Alfi, 2017. Unauthorized use or duplication of this material without express and written permission from the owner is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to Stav Alfi with appropriate and specific direction to the original content.
Creative Commons License "Lambda Expressions In Depth By Stav Alfi" by Stav Alfi is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. Based on a work at https://gist.github.com/stavalfi/e24178288973d104042d4162a02fd135.
Definition 1.1. Functional Interfaces are interfaces containing single abstract method and any number of default/static methods.
Definition 1.2. A function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value.
Definition 1.3. A function may be considered a pure function if both of the following statements about the function hold:
The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while the execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices.
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
Definition 2.1. A variable which wasn't defined as final, but never changed is also effectively final.
Definition 2.2. By referring free variables of a function is all the variables didn't define inside the function nor parameters of that function.
Definition 2.3. Lambda Expression is the method implementation of a functional interface. It has zero or more parameters and a body that will (or not) execute at a later stage. Inside the body, we have access to its free variables.
Variable scope | Read | Write | More Info |
---|---|---|---|
Lambda expression | T |
T |
|
Enclosing method | T |
F |
Must be final or effectivly final |
Enclosing class | T |
T |