Java 8 Notes

Java, originally evolved from the Oak language, was born in early 1996 with its major version as Java 1 or JDK 1.0. Java was initially designed and developed by James Gosling at Sun Microsystems. Java 8 or JDK 8.0 is one of the major releases of the Java programming language in 2014.

This article would walk you through new features were added in Java 8:

- Lambda Expressions: a new language feature allowing us to treat actions as objects.

- Method References: enable us to define Lambda Expressions by referring to methods directly using their names.

- Optional: special wrapper class used for expressing optionally

- Functional Interface: an interface with maximum one abstract method; implementation can be provided using a Lambda Expression

- Default methods: give us the ability to add full implementations in interfaces besides abstract methods

- Stream API: a special iterator class that allows us to process collections of objects in a functional manner

1. Lambda Expression

    parameter -> expression

    (param_1, param_2) -> expression

    (param_1, param_2) -> {code block}

VD:

import java.util.ArrayList;
import java.util.List;

public class Main {
  public static void main(String[] args) {
    List<Integer> numbers = new ArrayList<>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    numbers.forEach( (n) -> System.out.println(n));
  }
}

2. Method References

  • reference a method, reduce the verbosity of some lambdas.

  • using double colons:

  • there are two reference types:

Refer to static method

List<String> messages = Arrays.asList("hello", "phamduyhieu.com", "readers!")

// lambda:
messages.forEach(word -> StringUtils.capitalize(word));

// method reference
messages.forEach(StringUtils::capitalize);

Refer to instance method

public class BicycleComparator implements Comparator<Bicycle> {

    @Override
    public int compare(Bicycle o1, Bicycle o2) {
        return o1.getFrameSize().compareTo(o2.getFrameSize());
    }
}

ArrayList<Bicycle> myList = new ArrayList<>();
myList.add(new Bicycle("hieu", 20));
myList.add(new Bicycle("hieu", 30));
myList.add(new Bicycle("hieu", 40));
myList.add(new Bicycle("hieu", 10));

BicycleComparator myComparator = new BicycleComparator();

// lambda
List<Bicycle> newList = myList.stream().
                sorted((a, b) -> myComparator.compare(a, b)).collect(Collectors.toList());

// with method reference
List<Bicycle> newList2 = myList.stream().sorted(myComparator::compare).collect(Collectors.toList());

3. Optional

  • was created to avoid any runtime NullPointerExceptions, eliminate many null checks. Also, we can develop clean and neat APIs.
// before Optional

if (name != null) {
    System.out.println(name.length());
}

// with Optional
// deal with nullable values explicitly with a shorter way

opt.ifPresent(n -> System.out.println(n.length()));

// orElse() is used to return the wrapped value if it is present, and its argument otherwise

String nullName = null;
String nameTest = Optional.ofNullable(nullName).orElse("Hieu");
System.out.println(nameTest);

4. Functional Interfaces

  • is an interface with one single abstract method, no more, no less
  • support the lambda expression in java 8

  • Before java 8, we would usually create a class for every case where we needed to encapsulate a single piece of functionality => a lot of unnecessary boilerplate code.

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("my runnable");
    }
});

If you look at the above code, the actual part that is of use is the code inside run() method. Rest all of the code is because of the way java programs are structured.

  • Java 8 Functional interfaces and Lambda Expressions help us in writing smaller and cleaner code by removing a lot of boilerplate code.
Thread thread2 = new Thread(() -> System.out.println("my runnable"));

5. Default Method

  • is a method with an implementation which can be found in an interface.

  • to add a new functionality to an interface, while maintaining backward compatibility with classes that are already implementing the interface:

public interface Vehicle {
    public void move();
    default void hoot() {
        System.out.println("peep!");
    }
}
  • For example, the Collection interface can have a default implementation of the forEach method without requiring the classes implementing this interface to implement the same.

6. Streams

  • represents a sequence of objects from a source such as a collection, which supports aggregate operations.
int sum = Arrays.stream(new int[]{1, 2, 3, 4, 5})
        .filter(i -> i >= 3)
        .map(i -> i * 3)
        .sum();
  • in simple terms, a stream is an iterator whose role is to accept a set of actions to apply on each of the elements it contains.

  • Difference between Map and flatMap:

    Both map and flatMap are intermediate stream operations that receive a function and apply this function to all the elements of a stream. But the difference is that for the map, this function returns a value, but for flatMap, this function returns a stream.