LEPL1402 – Functional programming in Java
2023-12-07
You’ve already met functional programming in Python!
Image credits: https://realpython.com/python-functional-programming/
Why functional programming?
Functions + immutable data
Code that is easier to reason about (shorter, clarity…)
Improved uncoupling of code
Better handling of concurrency
Why functional programming?
Functions + immutable data
Code that is easier to reason about (shorter, clarity…)
Functional interfaces
Improved uncoupling of code
Lambda expressions
Better handling of concurrency
Stream API
Nesting classes
A basic spreadsheet
A basic spreadsheet
Spreadsheet document
Spreadsheet document
Keep rows sorted
after each modification
Spreadsheet document
Sorting using an external class:
Keep rows sorted
after each modification
Static nested classes: Bring class closer to method
Static nested classes: Bring class closer to method
This is the
“outer” class
Static nested classes: Bring class closer to method
This is the
“outer” class
“RowComparator2” has access to the private
static members of Spreadsheet
(enumerations, helpers...)
Static nested classes: Bring class closer to method
This is the
“outer” class
“RowComparator2” has access to the private
static members of Spreadsheet
(enumerations, helpers...)
Static nested classes
improve code
organization, “packages
at a finer granularity”
Static nested classes: Bring class closer to method
This is the
“outer” class
“RowComparator2” has access to the private
static members of Spreadsheet
(enumerations, helpers...)
Static nested classes
improve code
organization, “packages
at a finer granularity”
Problem: “sortOnColumn” is copied!
Inner classes: Give access to non-static members
Inner classes: Give access to non-static members
The class is not tagged as
static anymore!
Inner classes: Give access to non-static members
The class is not tagged as
static anymore!
The method now
has access to the
members of the
outer object
Inner classes: Give access to non-static members
The class is not tagged as
static anymore!
The method now
has access to the
members of the
outer object
No need to copy
“sortOnColumn”
anymore!
Inner classes: Give access to non-static members
The class is not tagged as
static anymore!
The method now
has access to the
members of the
outer object
No need to copy
“sortOnColumn”
anymore!
Syntactic sugar
Syntactic sugar
Java compiler
Syntactic sugar
Java compiler
Syntactic sugar
Java compiler
Syntactic sugar
Java compiler
Syntactic sugar refers to language features
that do not introduce new functionality
but provide a more convenient way of writing code
Inner classes keep a reference to their outer object
Code automatically generated for inner classes
Image credits: https://syntactic-sugar.netlify.app/
Local inner classes: Bring class inside the method
The comparator is entirely local to sort(), and cannot be used
in another method or class, which further reduces coupling
Anonymous inner classes
No need to give a name to the comparator if it is only used once!
Access to method variables (1/3)
Inner and anonymous classes have access to the variables of their method!
Example: Filling a matrix with multiple threads
Image credits: https://www.cuemath.com/algebra/solve-matrices/
Access to method variables (2/3)
The Filler class receives a copy
of the variables in fill1()
at its creation
Access to method variables (3/3)
Access to method variables (3/3)
Create one callable
per row to augment
parallelism
Access to method variables (3/3)
The inner class can only access
final variables (which row is not)
Create one callable
per row to augment
parallelism
Lambda expressions
A recurring pattern
A recurring pattern
A recurring pattern
A recurring pattern
A recurring pattern
Very stupid classes implementing
one single method!
Functional interfaces
A functional interface is defined as an interface that contains only one abstract method
Functional interfaces are also known as Single Abstract Method (SAM) interfaces
The interfaces Comparator<T>, ActionListener, Runnable, and Callable<T>
are all examples of functional interfaces
Functional interfaces are a key component of functional programming support
introduced in Java 8
Lambda expressions
A lambda expression is an expression that creates an instance of an
anonymous inner class that has no member and
that implements a functional interface
Lambda expressions
A lambda expression is an expression that creates an instance of an
anonymous inner class that has no member and
that implements a functional interface
Name of the
arguments
Lambda expressions
A lambda expression is an expression that creates an instance of an
anonymous inner class that has no member and
that implements a functional interface
Name of the Body implementing the
arguments functional interface
Lambda expressions
A lambda expression is an expression that creates an instance of an
anonymous inner class that has no member and
that implements a functional interface
Name of the Body implementing the
arguments functional interface
Thanks to the context, the compiler
knows that the functional interface
to be implemented is Comparator<Row>
Lambda expressions
A lambda expression is an expression that creates an instance of an
anonymous inner class that has no member and
that implements a functional interface
Name of the Body implementing the
arguments functional interface
Thanks to the context, the compiler
knows that the functional interface
to be implemented is Comparator<Row>
Lambda expressions for our recurring pattern
Comparator<T>
ActionListener
Runnable
General form of a lambda expression
General form of a lambda expression
Possible simplifications:
The compiler can generally infer the type of the arguments:
(a, b, c /*...*/) -> { /*...*/ }
If one argument: a -> { /*...*/ }
If no body: (a, b, c /*...*/) -> /* result */
If no return: (a, b, c /*...*/) -> /* body */
General form of a lambda expression
Possible simplifications: Method references:
The compiler can generally infer the type of the arguments:
(a, b, c /*...*/) -> { /*...*/ }
If one argument: a -> { /*...*/ }
If no body: (a, b, c /*...*/) -> /* result */
If no return: (a, b, c /*...*/) -> /* body */
General-purpose functions
Package java.util.function
General-purpose classes that provide a context to create and hold lambda expressions:
Unary functions: Function<T,R>
Binary functions: BiFunction<T,U,R>
Unary operators: UnaryOperator<T>
Binary operators: BinaryOperator<T>
Predicates: Predicate<T>
Consumers: Consumer<T>
...
Unary functions
Example 1
Example 2
Binary functions
Operators: Functions with one type
Predicates: Functions returning a Boolean
Consumers: Functions producing no result
Composition of functions or operators
Composition of predicates
Higher-order functions
Methods that can accept other functions as arguments,
return functions as results, or both
Higher-order functions
Methods that can accept other functions as arguments,
return functions as results, or both
In the context of collections:
The Iterable<T> interface has forEach() method to apply a consumer to a collection
The Collection<T> interface has removeIf() method to apply a predicate to filter a collection
The ArrayList<T> class has replaceAll() method to apply an operator to a collection
Higher-order functions
Methods that can accept other functions as arguments,
return functions as results, or both
In the context of collections:
The Iterable<T> interface has forEach() method to apply a consumer to a collection
The Collection<T> interface has removeIf() method to apply a predicate to filter a collection
The ArrayList<T> class has replaceAll() method to apply an operator to a collection
A bit like Python, but Streams (next week)
will bring Java even closer to Python!
Examples on collections
Implementing composition by ourselves