java8-slides



java8-slides

0 4


java8-slides

Slides about the new features of Java 8

On Github DDuarte / java8-slides

Introduction to New Features in Java 8

By Duarte Duarte (FEUP), Eduardo Martins (FEUP), Miguel Marques (FEUP), Ruben Cordeiro (FEUP) and Raffi Khatchadourian (City Tech)

Demonstration code at: http://github.com/khatchad/java8-demo.

Some History

  • Java was invented in the 90's as an Object-Oriented language.
  • Largest change was in ~2005 with Java 5.
    • Generics.
    • Enhanced for loops.
    • Annotations.
    • Type-safe enumerations.
    • Concurrency enhancements (AtomicInteger).

Java 8 is Massive

  • 10 years later, Java 8 is packed with new features.
  • Core changes are to incorporate functional language features.
  • We can't cover everything today.
  • Will focus on some of the more distributive features.

Functional Languages

  • Declarative ("what not how").
  • The only state is held in parameters.
  • Traditionally popular in academia and AI.
  • Well-suited for event-driven/concurrent ("reactive") programs.

Lambda Expressions

A block of code that you can pass around so it can be executed later, once or multiple times.

  • Anonymous methods.
  • Reduce 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

How are they different from Java methods?

Lambdas

(int x, int y) -> x + y

() -> 42

(String s) -> {System.out.println(s);}
- Omission of brackets and return statement - Return type deduction

Lambdas

Examples

Before

Button btn = new Button();
final PrintStream pStream = ...;
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent e) {
        pStream.println("Button Clicked!");
    }
});

After

Button btn = new Button();
final PrintStream pStream = ...;
btn.setOnAction(e -> pStream.println("Button Clicked!"));
- Argument type deduction - In-place implementation. - Local variable capturing - Multiple lines lambdas

Lambdas

Examples

List<String> strs = ...;
Collections.sort(strs, (s1, s2) -> 
    Integer.compare(s1.length(), s2.length()));
new Thread(() -> {
    connectToService();
    sendNotification();
}).start();

Functional Interfaces

Single Abstract Method Type

Functional Interfaces

Example

@FunctionalInterface
public interface Runnable {
    public 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

java.util.function

  • Predicate<T> - a boolean-valued property of an object
  • Consumer<T> - an action to be performed on an object
  • Function<T,R> - a function transforming a T to a R
  • Supplier<T> - provide an instance of a T (such as a factory)
  • UnaryOperator<T> - a function from T to T
  • BinaryOperator<T> - a function from (T,T) to T- More meaningful type names

Method References

Treating an existing method as an instance of a Functional Interface

- Object oriented way of attributing a method to a variable

Method References

Examples

class Person {
    private String name;
    private int age;

    public int getAge() {return this.age;}
    public String getName() {return this.name;}
}

Person[] people = ...;
Comparator<Person> byName = Comparator.comparing(Person::getName);
Arrays.sort(people, byName);
- :: operator

Method References

Kinds of method references

  • A static method (ClassName::methName)
  • An instance method of a particular object (instanceRef::methName)
  • A super method of a particular object (super::methName)
  • An instance method of an arbitrary object of a particular type (ClassName::methName)
  • A class constructor reference (ClassName::new)
  • An array constructor reference (TypeName[]::new)- "Instance method of an arbitrary object" adds an argument of that type which becomes the receiver of the invocation

Method References

More Examples

Consumer<Integer> b1 = System::exit;
Consumer<String[]> b2 = Arrays::sort;
Consumer<String> b3 = MyProgram::main;
Runnable r = MyProgram::main;

Default Methods

Add default behaviours to interfaces

Why default methods?

Java 8 has lambda expressions. We want to start using them:

List<?> list = ...
list.forEach(...); // lambda code goes here

There's a problem

The forEach method isn’t declared by java.util.List nor the java.util.Collection interface because doing so would break existing implementations.

Default Methods

We have lambdas, but we can't force new behaviours into the current libraries.

Solution: default methods.

Default Methods

  • Traditionally, interfaces can't have method definitions (just declarations).
  • Default methods supply default implementations of interface methods.

Examples

Example 1

Basics

public interface A {

    default void foo() {
        System.out.println("Calling A.foo()");
    }
}

public class Clazz implements A {
}

Client code

Clazz clazz = new Clazz();
clazz.foo();

Output

"Calling A.foo()"

Example 2

Getting messy

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?

Of course not (Java is not C++)!

class Clazz inherits defaults for foo() from both types A and B

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 a 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);

This is also valid for Sets and Queues, for example, since both classes implement the Iterable interface.

Summary

  • Default methods can be seen as a bridge between lambdas and JDK libraries.
  • Can be used in interfaces to provide default implementations of otherwise abstract methods.
    • Clients can optionally implement (override) them.
  • static methods are now also allowed in interfaces.
    • Can eliminate the need for utility classes like Collections.

Accumulators

Scalable updatable variables

Accumulators

The continual evolution of uses of concurrency and parallelism in applications requires continual evolution in library support. For this purpose, the Accumulators were introduced.

Accumulators

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:

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

Accumulators

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.

Accumulators

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).

Examples

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: 20
LongAdder la = new LongAdder();
List<Long> longs = Arrays.asList(10L, 20L, 30L, 40L, 100L);
longs.forEach(la::add);

System.out.println("Result: " + la.longValue());

Output:

Result: 200

java.util.stream

filter/map/reduce for Java

java.util.stream

List<Student> students = …;
Stream stream = students.stream(); // sequential version

// parallel version
Stream parallelStream = students.parallelStream();
  • traversed once
  • infinite
  • lazy

java.util.stream

Stream sources

Collections

Set<Student> set = new LinkedHashSet<>();
Stream<Student> stream = set.stream();

Generators

Random random = new Random();
Stream<Integer> randomNumbers = Stream.generate(random::nextInt);

From other streams

Stream newStream = Stream.concat(stream, randomNumbers);

java.util.stream

Intermediate operations

  • .filter - excludes all elements that don’t match a Predicate
  • .map - perform transformation of elements using a Function
  • .flatMap - transform each element into zero or more elements by way of another Stream
  • .peek - performs some action on each element
  • .distinct - excludes all duplicate elements (equals())
  • .sorted - orderered elements (Comparator)
  • .limit - maximum number of elements
  • .substream - range (by index) of elements
List<Person> persons = ...;
Stream<Person> tenPersonsOver18 = persons.stream()
                           .filter(p -> p.getAge() > 18)
                           .limit(10);

More examples at: https://github.com/cst2301-pt13/library-solution.

java.util.stream

Terminating operations

Obtain a stream from some sources Perform one or more intermidate operations Perform one terminal operation
  • reducers like reduce(), count(), findAny(), findFirst()
  • collectors (collect())
  • forEach
  • iterators
List<Person> persons = ..;
List<Student> students = persons.stream()
                           .filter(p -> p.getAge() > 18)
                           .map(Student::new)
                           .collect(Collectors.toList());

java.util.stream

Parallel & Sequential

List<Person> persons = ..;
List<Student> students = persons.stream()
         .parallel()
         .filter(p -> p.getAge() > 18)
         .sequential()
         .map(Student::new)
         .collect(Collectors.toCollection(ArrayList::new));

java.time

Yet Another Java Date/Time API

java.time

Current Time

Clock clockUTC = Clock.systemUTC();
Clock clockDefault = Clock.systemDefaultZone();
- Different Time Zones

java.time

Time Zones

ZoneId zone = ZoneId.systemDefault();
ZoneId zoneBerlin = ZoneId.of("Europe/Berlin");
Clock clock = Clock.system(zoneBerlin);
- Constructing a Clock using a ZoneId

java.time

Human Readable Time

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

java.time

Doing Calculations

LocalTime lt = LocalTime.now();
lt.plus(5, ChronoUnit.HOURS);

lt.plusHours(5);

Duration dur = Duration.of(5, ChronoUnit.HOURS);
lt.plus(dur);

References & Documentation & Interesting Links

Questions?