Java 8 includes new functional features such as lambda expressions, method references, and streams. Lambda expressions allow for the creation of anonymous functions, and method references provide a way to refer to existing methods. Streams facilitate functional-style aggregate operations on data and support both sequential and parallel operations. Other features include default interface methods, closures, and static methods on interfaces.
2. Francisco Ortín Soler
Disclaimer
• This slides are aimed at briefly explaining the new
functional features of Java 8
• It is an informal document
• The code used in this slides is available at
http://www.reflection.uniovi.es/download/2014/java8.zip
• It has been compiled and executed with Java SE
Development Kit 8.0
March 20th, 2014
Francisco Ortin
ortin at lsi.uniovi.es
3. Francisco Ortín Soler
Java 8
• Java 8 has been released on March 2014
• It includes some features of the functional
paradigm such as:
Lambda expressions
Method references
Types of some typical lambda expressions
Streams (aggregate operations)
Closures (constant variables of the enclosing block)
• It also provides default method implementations
for interfaces
4. Francisco Ortín Soler
Lambda Expressions
• Lambda expressions are provided
The -> symbol separates parameters from body
Parameter types can be optionally specified
Parenthesis are not mandatory when only one
parameter is passed
If the body is just one expression, return and { }
are not required
String[] words = new String [] {
"Hello", "from", "Java", "8" };
Arrays.sort(words,
(word1, word2) -> word1.length() - word2.length()
);
5. Francisco Ortín Soler
Types of Lambda Expressions
• Lambda expressions promote to interfaces with
one abstract method with the same signature as
the lambda expression
• This kind of interfaces are called Functional
Interfaces
The @FunctionalInterface annotation can be used
It is optional; helpful for detecting errors
@FunctionalInterface // not mandatory
interface MyPredicate<T> {
boolean exec(T element);
}
6. Francisco Ortín Soler
Types of Lambda Expressions
@FunctionalInterface // not mandatory
interface MyPredicate<T> {
boolean exec(T element);
}
class Promotion {
static <T> T find(T[] collection, MyPredicate<T> predicate) {
for(T item : collection)
if (predicate.exec(item))
return item;
return null;
}
public static void main(String... args) {
Integer[] numbers = new Integer [] { 1, 2, 3 };
int number = find(numbers, n -> n % 2 == 0);
System.out.println(number);
}
}
7. Francisco Ortín Soler
Method References
• Sometimes, a lambda expression does nothing but calling
an existing method
• In those cases, the existing method can be referred by
name
• For this purpose, the :: operator has been added to Java 8
• Static (class) methods are referred with Class::method
class MethodReferences {
static boolean isOdd(Integer number) {
return number %2 != 0;
}
public static void main(String... args) {
Integer[] numbers = new Integer [] { 1, 2, 3 };
Integer number = Promotion.find(numbers, MethodReferences::isOdd);
number = Promotion.find(numbers, new EqualTo(3)::compare);
}
}
8. Francisco Ortín Soler
Method References
• Instance methods are referred with object::method
• Since these methods are associated to an object (this),
they can be stateful
class EqualTo {
private int value;
public EqualTo(int value) { this.value = value; }
public boolean compare(Integer n) { return value == n; }
}
public class MethodReferences {
public static void main(String... args) {
Integer[] numbers = new Integer [] { 1, 2, 3 };
Integer number = Promotion.find(numbers,
new EqualTo(3)::compare);
}
}
9. Francisco Ortín Soler
Types of Typical Lambda Exprs
• The package java.util.function provides types
(functional interfaces) of typical lambda functions
Function<T,R>: Function that receives a T argument
and returns a R result
Predicate<T>: Predicate of one T argument
Consumer<T>: An operation that accepts a single T
argument and returns no result
Supplier<T>: Function with no parameter returning
a T value
UnaryOperator<T>: Operation on a single T operand,
producing a T result
BinaryOperator<T>: Operation upon two T
operands, producing a result of the same type as
the operands
10. Francisco Ortín Soler
Types of Typical Lambda Exprs
• Notice: the methods of the interfaces must be explicitly called, and
they are named differently (test, accept, apply, get…)
public static void main(String... args) {
MyPredicate<Integer> even = n -> n%2 == 0; // my own type
Predicate<Integer> odd = n -> n%2 != 0;
System.out.println(even.exec(number) + " " + odd.test(number));
Consumer<Integer> printAction = n -> System.out.println(n);
printAction.accept(number);
Function<Integer,Double> sqrt = n -> Math.sqrt(n);
System.out.println(sqrt.apply(number));
Supplier<Integer> random = () -> (int)(Math.random()*1000 - 1000/2);
System.out.println(random.get());
BinaryOperator<Integer> times = (a,b) -> a*b;
System.out.printf(times.apply(3,2));
11. Francisco Ortín Soler
Types of Typical Lambda Exprs
• Since generics is implemented in Java with type
erasure (i.e., T is Object), the previous types
have specific versions for built-in types:
And more…
http://download.java.net/jdk8/docs/api/java/util/function/package-summary.html
Predicate<T> Supplier<T> Consumer<T> Function<T,R>
DoublePredicate BooleanSupplier DoubleConsumer DoubleFunction<R>
IntPredicate DoubleSupplier IntConsumer IntFunction<R>
LongPredicate IntSupplier LongConsumer IntToDoubleFunction
LongSupplier IntToLongFunction
LongFunction<R>
…
12. Francisco Ortín Soler
Streams with Aggregate Operations
• The new java.util.stream package provides an API to
support functional-style operations on streams
• A stream is a sequence of elements
It is not a data structure that stores elements (i.e. a
collection)
• They support sequential and parallel functional-style
aggregate operations
• Operations are composed into a stream pipeline
• Pipeline consists of
A source (array, collection, generator, I/O channel…)
Intermediate aggregate operations
And a terminal operation, producing a result
• Computation on the source data is only performed when
the terminal operation is initiated (kind of lazy)
13. Francisco Ortín Soler
Streams (Aggregate Operations)
public class Streams {
static int compute(Collection<Integer> collection) {
return collection.stream()
.filter(n -> n%2 == 0) // even numbers
.map(n -> n*n) // square
.reduce(0, (acc, item) -> acc + item); // summation
}
public static void main(String... args) {
System.out.println(compute(Arrays.asList(1, 2, 3, 4, 5)));
System.out.println(Arrays.asList(
Stream.iterate(1, n -> n+1)
.skip(10)
.limit(5)
.toArray(Integer[]::new)
));
}
}
source
aggregate operations
terminal operation
source (infinite)
aggregate operations
terminal operation
• Similar to .NET LINQ
• There will be database streams eventually?
14. Francisco Ortín Soler
Closures
• Lambda expressions can capture variables of the
enclosing scope
• They do not have shadowing issues (a new scope is not
created, being lexically scoped)
• Captured variables must be final or effectively final (their
value cannot be modified)
public class Closures {
static Function<Integer,Integer> createClosure(int initialValue) {
int number = initialValue; // must be constant
return n -> number + n;
}
public static void main(String... args) {
Function<Integer,Integer> closure1 = createClosure(1);
System.out.println(closure1.apply(7) );
Function<Integer,Integer> closure10 = createClosure(10);
System.out.println(closure10.apply(7) );
}
}
15. Francisco Ortín Soler
Closures
• Since functions are objects, they can represent functions
with a mutable state
class Fibonacci implements Supplier<Integer> {
private int previous = 0, current = 1;
@Override
public Integer get() {
int next = current + previous;
previous = current;
current = next;
return previous;
}
public static void main(String... args) {
System.out.println(Arrays.asList(
Stream.generate(new Fibonacci()).limit(10)
.toArray(Integer[]::new)
));
}
}
16. Francisco Ortín Soler
Default Methods
• Java 8 provides default implementations for interface
methods (the default keyword is used), similar to mixins
@FunctionalInterface interface Comparator<T> {
int compare(T a, T b);
default Comparator<T> reversed() { return (a, b) -> this.compare(b,a); }
}
public class DefaultMethods {
public static <T> T max(T a, T b, Comparator<T> comp) {
return comp.compare(a,b)<0 ? a : b;
}
public static <T> T min(T a, T b, Comparator<T> comp) {
return max(a, b, comp.reversed());
}
public static void main(String... args) {
Comparator<String> comparator = (a,b) -> a.length() - b.length();
System.out.println(max("hello", "bye", comparator));
System.out.println(min("hello", "bye", comparator));
} }
17. Francisco Ortín Soler
Multiple Inheritance
• As with multiple inheritance languages, different
implementations of the same method may be
inherited
• However, the Java compiler checks this condition,
reporting an error
interface A {
default void m() { System.out.println("A::m"); }
}
interface B {
default void m() { System.out.println("B::m"); }
}
public class MultipleInheritance
implements A, B { // compiler error
}
18. Francisco Ortín Soler
Multiple Inheritance
• Besides, a default method cannot be inherited if
the class implements another interface with that
method (even without a default implementation)
interface A {
default void m() { System.out.println("A::m"); }
}
interface C {
void m();
}
class MyClass implements A, C { // compiler error
}
19. Francisco Ortín Soler
Multiple Inheritance
• Java 8 allows diamond inheritance: the most
specific (derived) method implementation is called
interface A {
default void m() { System.out.println("A::m"); }
}
interface A1 extends A {}
interface A2 extends A {
default void m() { System.out.println("A2::m"); }
}
class Diamond implements A1, A2 {
public static void main(String... args) {
new Diamond().m(); // A2::m
A1 a1 = new Diamond();
a1.m();
} }
20. Francisco Ortín Soler
Static Methods
• Java 8 allows interfaces to implement static methods to
provide utility methods
• The static methods specific to an interface can be kept in
the same interface rather than in a separate class
@FunctionalInterface
interface Comparator<T> {
int compare(T a, T b);
static <T extends Comparable<T>> Comparator<T> naturalOrder() {
return (a,b) -> a.compareTo(b);
}
}
public class DefaultMethods {
public static void main(String... args) {
System.out.println(
max("hello", "bye", Comparator.naturalOrder()
));
} }