New Way to Program in Java

 

λ

Java 8 New Features

  • Default Methods
  • Lambda Expression
  • Fundamentals Of FP
  • Optional API
  • Stream API

Default Method

In Java 7, your API design can only like this

public interface Toxic {

    void eatTamJai();

    void surfGolden();

    int findFriends();

    int findGirlFriends();
}
public class Gap implements Toxic {

  @Override
  public void eatTamJai() {
    System.out.println("Chicken rice noodle with medium spicy");
  }

  @Override
  public void surfGolden() {
    System.out.println("CD-ROM");
  }

  @Override
  public int findFriends(){
    return 0;
  }

  @Override
  public int findGirlFriends(){
    return 0;
  }
}

Java 7

public class Peter implements Toxic {

  @Override
  public void eatTamJai() {
    System.out.println("Beef rice noodle with ching soup");
  }

  @Override
  public void surfGolden() {
    System.out.println("Always LM");
  }

  @Override
  public int findFriends(){
    return 0;
  }

  @Override
  public int findGirlFriends(){
    return 0;
  }

}

Problems???

Default Method

Java 7

public class Peter implements Toxic {

  @Override
  public void eatTamJai() {
    System.out.println("Beef rice noodle with ching soup");
  }

  @Override
  public void surfGolden() {
    System.out.println("Always LM");
  }

  @Override
  public int findFriends(){
    return 0;
  }

  @Override
  public int findGirlFriends(){
    return 0;
  }

}

A Toxic Person should

ALWAYS

Friends  = 0 && GirlFriends = 0 !!!

In Real world, some properties/behaviours are always the same...

Default Method

Java 7

Default Method

In Java 8,

You can have a default implemetation in a interface.

 

  •  Setup the default behavior of the object
public interface Toxic {

    void eatTamJai();

    void surfGolden();

    default int findFriends(){
        return 0;
    }

    default int findGirlFriends(){
        return 0;
    }
}
public class Gap implements Toxic {

  @Override
  public void eatTamJai() {
    System.out.println("Chicken rice noodle with medium spicy");
  }

  @Override
  public void surfGolden() {
    System.out.println("CD-ROM");
  }
    
}

Java 8

Default Method

public interface Programmer {

  default int findGirlFriends(){
    return 0;
  }

}
public class Gap implements Toxic, Programmer {

  @Override
  public void eatTamJai() {
    System.out.println("Chicken rice noodle with medium spicy");
  }

  @Override
  public void surfGolden() {
    System.out.println("CD-ROM");
  }
    
}

How about two interfaces have the same behaviours?

Java 8

Default Method

public interface Programmer {

  default int findGirlFriends(){
    return 0;
  }

}
public class Gap implements Toxic, Programmer {

  @Override
  public void eatTamJai() {
    System.out.println("Chicken rice noodle with medium spicy");
  }

  @Override
  public void surfGolden() {
    System.out.println("CD-ROM");
  }

  @Override
  public int findGirlFriends() {
    return Programmer.super.getGirlFriends();
  }  
}

Java 8

public interface Toxic {

    void eatTamJai();

    void surfGolden();

    default int findFriends(){
        return 0;
    }

    default int findGirlFriends(){
        return 0;
    }
}

Default Method

Java 8 Default Method vs Abstract Class

Java 7 Interface Java 8 Interface Abstract Class
Method signatures
Concrete methods
Constructor
 
Member variables

Java 8 New Features

  • Default Methods
  • Lambda Expression
  • Fundamentals Of FP
  • Optional API
  • Stream API

Lambda Expression

λ

lambda is just an anonymous function

Lambda Expression

λ

 Java <= 7

 

  • Strictly-OOP
  • NO Anonymous Function (Popular to use Anonyomous class to substitute)
  • Difficult to write functional style code
  • Difficult to write high-order function

Lambda Expression

λ

With Java 8 Lambdas, we can ...

  • Have Anonymous Function
  • Cleaner representation compared with Anonymous class
  • Code faster
  • Produce Functional-style code
  • Easier to write High-order functions ( Fundamental of Functional programming)

Lambda Expression

λ

//Lambda Expression syntax should like this
(arg1, arg2, ... ) -> { body }

// Or with Type
(type1 arg1, type2 arg2) -> { body }

Very Common Functional Interface in Java 8

Consumer, Predicate, Supplier, Function

Java 8 New Features

  • Default Methods
  • Lambda Expression
  • Fundamentals Of FP
  • Optional API
  • Stream API

Fundamentals of FP

  1. Raise of Multi-core and distributed computing
  2. Simplicity, correctness and maintainability
  3. Commercial success by using functional language (ie. Twitter/Netflix - Scala, WhatsApp/LoL - Erlang) 

Why more people talk about Functional Programming recently?

λ

Fundamentals of FP

  1. Avoids changing-state and mutable data
  2. Eliminating side effects (Write Pure Function)

Significant Conceptual Differences to Imperative Programming (i.e. OOP) 

λ

Fundamentals of FP

Characteristic Imperative Functional
Programmer focus How to track changes in state. What transformations are required.
State changes Important Non-existent.
Primary flow control Loops, conditionals, and function calls. Function calls
Primary manipulation  Instances of structures or classes. Functions as first-class objects and data collections

λ

public void addEvenUserIdToList(List<User> users, List<Integer> ids){

    for(User user : users){
        if(user.getId() % 2 == 0)
            ids.add(user.getId());
    }

}
public List<Integer> filterEvenAndTransformToUserId(List<User> users){

    return users
            .stream()
            .map(User::getId)
            .filter(id -> id % 2 == 0)
            .collect(Collectors.toList());
}

λ

Imperative style

Functional style

Slide effects!!!!  Not pure function

Collect as NEW objects!

No Side Effects => Pure function

Fundamentals of FP

Fundamentals of FP

Advantages of Pure Functions

  • Increased readability and maintainability
  • Easier to refactor, changes
  • Easier testing and debugging

λ

Fundamentals of FP

High Order Function Example In Java 8

// f(x) = x + 1
Function<Integer,Integer> f = (x) -> x + 1;

// g(x) = x * x
Function<Integer,Integer> g = (x) -> x * x;

// h(x) = x * 2 
Function<Integer,Integer> h = (x) -> x * 2;

// h(g(f(x))
Function<Integer,Integer> func = h.compose(g).compose(f);

// Integer to Double function
Function<Integer,Double> toDouble = Integer::doubleValue;

// toDouble(h(g(f(x)))
Function<Integer,Integer> funcWithLogging = func.andThen(toDouble);

λ

Fundamentals of FP

High Order Function Example In Java 8

  @Test
  public void test(){
    // h(g(f(x)), where h(x) = x * 2 , g(x) = x * x , f(x) = x + 1;
    int result = HighOrderFunctionExample.func.apply(1);

    // h(g(f(1)) => h(g(2)) => h(4) => 8
    assertEquals(result,8);
  }

  @Test
  public void testWithLogging(){
    double result = HighOrderFunctionExample.funcThenToDouble.apply(1);

    assertEquals(result,8,0.001);
  }

λ

Fundamentals of FP

Similar In Java 7 ?

public Integer f(Integer x) {
  return x + 1;
}
public Integer g(Integer x) {
  return x * x;
}
public Integer h(Integer x) {
  return x * 2;
}
public Integer func(Integer x){
  return h(g(f(x)));
}

public Double toDouble(Integer x){
  return x.doubleValue();
}

public Double funcThenToDouble(Integer x){
  return toDouble(func(x));
}

λ

public List<Integer> filterEvenAndTransformToUserId(List<User> users){

    return users
            .stream()
            .map(User::getId)
            .filter(id -> id % 2 == 0)
            .collect(Collectors.toList());
}

Fundamentals of FP

Real World High order function In Java 8

λ

Predicate<Integer> filterEventNumber =  x -> x % 2 == 0;

public List<Integer> filterEvenAndTransformToUserId2(List<User> users){
    return users
      .stream()
      .map(User::getId)
      .filter(filterEventNumber)
      .collect(Collectors.toList());
}

Java 8 New Features

  • Default Methods
  • Lambda Expression
  • Fundamentals Of FP
  • Optional API
  • Stream API

Optional API

  1. Replace null  to avoid NullPointerException
  2. More meaningful than null
  3. Clearer code when doing Stream

Optional can ...

Optional API

Tony Hoare  :

"I call it my billion-dollar mistake. It was the invention of the null reference in 1965."

"This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years"

Optional API

Before Java 8, Guava @Google has implemented Optional to avoid null

  • null is unpleasantly ambiguous
  • null gives no indication what a null value means
  •  it is a major source of confusion, difficult and weird bugs, and unpleasant ambiguities 

Reason explained by Guava Team: 

Optional API

Why Optional ?

  • Increase readability
  • idiot-proof-ness
  • force you actively think about the absent case

Optional API

Replace null without rewriting root API

  public User defaultValueUseCase1(){
    User user = DB.findUserById(1);

    if(user == null) user = new User(1,"a");

    return user;
  }

Use `Optional.ofNullable` wrap the object that maybe null

  public User defaultValueUseCase1InOptional() {
    return Optional.ofNullable(DB.findUserById(1))
            .orElse(new User(1,"a"));
  }

Use Case : Default Value

Use `Optional.orElse` to specify default value

Optional API

Replace null without rewriting root API

  public User throwExceptionUseCase2() throws Exception {
    User user = DB.findUserById(1);

    if(user == null) throw new Exception();

    return user;
  }

Use `Optional.orElseThrow` to throw exception

  public User throwExceptionUseCase2InOptional() throws Exception {
    return Optional.ofNullable(DB.findUserById(1))
            .orElseThrow(Exception::new);
  }

Use Case : Exception Handling

Use `Optional.ofNullable` wrap the object that maybe null

Optional API

Replace null by rewriting root API

public class DB {

  public static User findUserById(int id){
    for(User u : users){
      if(u.getId() == id)
        return u;
    }
    return null;
  }

  //rewrite to return Optional
  public static Optional<User> findUserByIdOpt(int id){
    for(User u : users){
      if(u.getId() == id)
        return Optional.of(u);
    }
    return Optional.empty();
  }

}

Use `Optional.of` wrap the object that must NOT null

Optional API

Streaming is very common

I need to find Object C

For example :

Look up Object C depends on Object B

Look up Object B depends on Object A

Finally, the look up sequence : A -> B -> C

Optional API

Java 7

    public Country findUserCountry(User user){
    
        if(user != null){
    
            String countryId = user.getCountryId();
    
            if(countryId != null){
                return DB.findCountryById(countryId);
            }
        }
        return null;
    }

Real World Example, 

User -> Country ID -> Country

Streaming is very common

Problems: Easy to miss, difficult to read

Optional API

Java 8

  public Optional<Country> findUserCountryOpt(User user){
    return Optional.ofNullable(user)
            .map(User::getCountryId)
            .map(DB::findCountryById);
  }

Version 1, Return a Optional Value

Version 2, Throws Exception when not found

public Country findUserCountry(User user) throws Exception {
    return Optional.ofNullable(user)
            .map(User::getCountryId)
            .map(DB::findCountryById)
            .orElseThrow(Exception::new);
}

Streaming is very common

Optional API

Java 8

Other Useful API


Optional<Integer> optional = Optional.of(1);

// Check the value exist in the Optional
optional.isPresent();    // return true;

// print the value if it exist
optional.ifPresent( x -> System.out.println(x));

// Apply filter ( x = 0 ) 
optional.filter(x -> x == 0).isPresent(); // return false;

// Flat & Map
optional.flatMap((x) -> Optional.of( x * x ));

Optional API

When NOT to use Optional ?

Don't use it if you are really processing Huge DataSet!

Using Oracle JMH for micro-benchmark : 3ns slower 

  @Benchmark
  public Integer vanillaNullSafeAndDefaultValue(){
    if(a == null){
      a = 1;
    }
    return a;
  }

  @Benchmark
  public Integer nullSafeAndDefaultValue(){
    return Optional.ofNullable(a).orElse(1);
  }

Java 8 New Features

  • Default Methods
  • Lambda Expression
  • Fundamentals Of FP
  • Optional API
  • Stream API

Stream API

What is Streams ?

  • No Storage
  • Designed for functional programming
  • Lazy Operation
  • Use ONLY once

Stream API

Two Type Operation

Data Source

Intermediate Operation

(filter/map/FlatMap/distinct/skip/sorted...)

Terminal Operation

(reduce/collect /forEach/...)

Stream API

Intermediate Operation is Lazy

Until 

Terminal Operation is applied

Stream.of(5,4,2,1,3)
      .filter(x -> x > 3)
      .sorted()
      .forEach(x -> System.out.println(x));
//Output
4
5

Lazy is Good

Stream API

Lazy is Good

  public void run(){
    List<Integer> list = Arrays.asList(1,2,3,4);
    multipleTwo(filterEvenNumber(list)).forEach(System.out::println);
  }

  public List<Integer> filterEvenNumber(List<Integer> list){

    List<Integer> tmp = new ArrayList<>();

    for(Integer x : list){
      System.out.println("iterating " + x);
      if(x % 2 == 0){
        tmp.add(x);
      }
    }
    return tmp;
  }

  public List<Integer> multipleTwo(List<Integer> list){
    List<Integer> tmp = new ArrayList<>();

    for(Integer x : list){
      System.out.println("iterating " + x);
      tmp.add(x *x);
    }

    return tmp;
  }

Java 7

//Output
iterating 1
iterating 2
iterating 3
iterating 4
iterating 2
iterating 4
4
16

iterated 6 times !!!

Stream API

Lazy is Good

public void run(){
    List<Integer> list = Arrays.asList(1,2,3,4);

    Stream<Integer> rawStream = 
        list
          .stream()
          .peek(x -> System.out.println("iterating " + x));

    Stream<Integer> stream = multipleTwo(filterEvenNumber(rawStream));

    stream.forEach(System.out::println);
}

public Stream<Integer> filterEvenNumber(Stream<Integer> list){
    return list.filter(x -> x % 2 == 0);
}

public Stream<Integer> multipleTwo(Stream<Integer> list){
    return list.map(x -> x * x);
}

Java 8

//Output
iterating 1
iterating 2
4
iterating 3
iterating 4
16

Iterate 4 times = Size of List !!

Stream API

Lazy is Good

Java 8

  public void functionalStyleRun(){
    List<Integer> list = Arrays.asList(1,2,3,4);
    list
      .stream()
      .filter(x -> x % 2 == 0)
      .map(x -> x * x)
      .forEach(System.out::println);
  }

Simplified Version with Anonyomus function

  Predicate<Integer> evenFilter = x -> x % 2 == 0;
  Function<Integer,Integer> mult2 = x -> x * x ;
  
  public void functionalStyleRun(){
    List<Integer> list = Arrays.asList(1,2,3,4);
    list
      .stream()
      .filter(evenFilter)
      .map(mult2)
      .forEach(System.out::println);
  }

Simplified Version with Decleared function

Stream API

Powerful API


  public List<Integer> distinct(List<Integer> list){
    return list
            .stream()
            .distinct()
            .collect(Collectors.toList());
  }

  public Map<String,List<User>> listToMap(List<User> users){

    return users
            .stream()
            .collect(Collectors.groupingBy(
              User::getCountryId,
              Collectors.toList()
            ));
  }

Collectors.*  are awesome

Stream API

Powerful API

Happy Java Life !!!

Question ?

You can found all the examples in the following repository

git clone https://github.com/gaplo917/java8-intro

// modify & run the test to learn more Java 8!!

New Way to Program in Java 8

By Gap撈Tech

New Way to Program in Java 8

Default Method, Lambda Expression, Functional programming style, Optional API, Stream API

  • 629