On Github DDuarte / java8-presentation
By Duarte Duarte, Eduardo Martins, Miguel Marques and Ruben Cordeiro
(these slides: http://goo.gl/h3C9N5) ESOF/MIEIC/FEUP - 2013/2014
Anonymous methods aimed at reducing the verbosity caused by anonymous classes.
- Alonzo Church in his invention of the lambda calculus in 1936. - Lisp 1958 - Supported in C#, JavaScript, Python, Ruby, C++11(int x, int y) -> x + y () -> 42 (String s) -> { System.out.println(s); }- Omission of brackets and return statement - Return type deduction
JButton btn = new JButton(); final PrintStream pStream = ...; btn.addActionListener(new ActionListener { @Override public void actionPerformed(ActionEvent e) { pStream.println("Button Clicked!"); } });
JButton btn = new JButton(); final PrintStream pStream = ...; btn.addActionListener(e -> pStream.println("Button Clicked!"));- Argument type deduction - In-place implementation. - Local variable capturing - Multiple lines lambdas
ArrayList<String> strs = ...; Collections.sort(strs, (s1, s2) -> s2.length() - s1.length());
new Thread(() -> { connectToService(); sendNotification(); }).start();
Single Abstract Method Type
@FunctionalInterface public interface Runnable { public abstract void run(); } Runnable r = () -> System.out.println("Hello World!");- @FunctionalInterface - May be omitted - Generates error when there is more than one abstract method - Attributing a lambda expression to a variable - Returning a lambda expression is also possible
Treating an existing method as an instance of a Functional Interface
- Object oriented way of attributing a method to a variableclass Person { private String _name; private int _age; public int getAge() { return _age; } public String getName() { return _name; } } Person[] people = ...; Comparator<Person> byName = Comparator.comparing(Person::getName); Arrays.sort(people, byName);- :: operator
Consumer<Integer> b1 = System::exit; Consumer<String[]> b2 = Arrays::sort; Consumer<String> b3 = MyProgram::main; Runnable r = MyProgram::main;
Add default behaviours to interfaces
Suppose Java 8 is out and has lambdas. We want to start using them:
List<?> list = ... list.forEach(...); // lambda code goes here
The forEach method isn’t declared by java.util.list nor the java.util.collection interface yet.
Adding extra methods to the java.util.collection interfaces would break the existing implementations.
We have lambdas, but we can't force new behaviours into the current libraries.
Solution: default methods.
Default methods or defender methods, can now be added to interfaces providing a default implementation of the declared behavior.
public interface A { default void foo() { System.out.println("Calling A.foo()"); } } public class Clazz implements A { }
Clazz clazz = new Clazz(); clazz.foo();
"Calling A.foo()"
public interface A { default void foo(){ System.out.println("Calling A.foo()"); } } public interface B { default void foo(){ System.out.println("Calling B.foo()"); } }
public class Clazz implements A, B { }
Does this code compile?
java: class Clazz inherits unrelated defaults for foo() from types A and B
Of course not!
How do we fix it?
Option A:
public class Clazz implements A, B { public void foo(){} }
We resolve it manually by overriding the conflicting method.
Option B:
public class Clazz implements A, B { public void foo(){ A.super.foo(); // or B.super.foo() } }
We call the default implementation of method foo() from either interface A or B instead of implementing our own.
Going back to the example of forEach method, how can we force it's default implementation all the iterable collections?
Let's take a look at the Java UML for all the iterable Collections:
In order to add a default behaviour to all the iterable collections, a default forEach method was added to the Iterable<E> interface.
We can find its default implementation in java.lang.iterable interface:
@FunctionalInterface public interface Iterable { Iterator iterator(); default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } }
The forEach method takes java.util.function.Consumer functional interface type as a parameter which enables us to pass in a lambda or a method reference as follows:
List<?> list = ... list.forEach(System.out::println);
However, this is also valid for object of class Set<E> or Queue<E> for example, since both classes implement the Iterable interface.
Default methods can be seen as a bridge between lambdas and JDK libraries. However, the scope of this feature surpasses the retrocompatibility domain: they can also help reduce the amount of redudant code when dealing with libraries like Swing.
Scalable updatable variables
The continual evolution of uses of concurrency and parallelism in applications requires continual evolution in library support. For this purpose, the Accumulators were introduced.
Maintaining a single count, sum, etc., that is updated by possibly many threads is a common scalability problem.
A set of new classes were created for that purpose:
Package java.util.concurrent mechanisms synchronized operations between threads, however if all you want to do is increment a variable across threads, it was overkill and then some.
These classes are usually preferable to alternatives when multiple threads update a common value that is used for purposes such as summary statistics that are frequently updated but less frequently read.
Both the DoubleAdder and LongAdder classes can be seen as specific subsets of the DoubleAccumulator and LongAccumulator functionality.
The call new DoubleAdder() is equivalent to
new DoubleAccumulator((x, y) -> x + y, 0.0).
The call new LongAdder() is equivalent to
new LongAccumulator((x, y) -> x + y, 0L).
DoubleAccumulator da = new DoubleAccumulator((x,y) -> x + y, 0.0); List<Double> doubles = Arrays.asList(1.0, 2.0, 3.0, 4.0, 10.0); doubles.forEach(da::accumulate); System.out.println("Result: " + da.doubleValue());
Output:
Result: 24
LongAdder la = new LongAdder(); List<long> longs = Arrays.asList(10, 20, 30, 40, 100); longs.forEach(la::accumulate); System.out.println("Result: " + la.longValue());
Output:
Result: 200
filter/map/reduce for Java
ArrayList<Student> students = …; Stream stream = students.stream(); // sequential version // parallel version Stream parallelStream = students.parallelStream();
LinkedHashSet set; Stream stream = set.stream();
Random random = new Random(); Stream randomNumbers = Stream.generate(random::nextInt);
Stream newStream = Stream.concat(stream, randomNumbers);
List persons = ...; Stream tenPersonsOver18 = persons.stream() .filter(p -> p.getAge() > 18) .limit(10);
List<Person> persons = ..; List<Student> students = persons.stream() .filter(p -> p.getAge() > 18) .map(Student::new) .collect(Collectors.toList());
List<Person> persons = ..; List<Student> students = persons.stream() .parallel() .filter(p -> p.getAge() > 18) .sequential() .map(Student::new) .collect(Collectors.toCollection(ArrayList::new));
Yet Another Java Date/Time API
Clock clockUTC = Clock.systemUTC();
Clock clockDefault = Clock.systemDefaultZone();- Different Time Zones
ZoneId zone = ZoneId.systemDefault();
ZoneId zoneBerlin = ZoneId.of("Europe/Berlin"); Clock clock = Clock.system(zoneBerlin);- Constructing a Clock using a ZoneId
class LocalDate { public static LocalDate now() { ... } public static LocalDate now(ZoneId zone) { ... } public static LocalDate now(Clock clock) { ... } }
LocalDate date = LocalDate.now(); System.out.printf("%s-%s-%s", date.getYear(), date.getMonthValue(), date.getDayOfMonth());- now() accepts a Clock or a ZoneId
LocalTime lt = LocalTime.now(); lt.plus(5, ChronoUnit.HOURS); lt.plusHours(5); Duration dur = Duration.of(5, ChronoUnit.HOURS); lt.plus(dur);