FunctionalOptionalStream & Vegtables

@FunctionalInterface

One method to rule them all

public class AnonymousInnerClassTest {
  public static void main(String[] args) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        System.out.println("A thread created and running ...");
      }
    }).start();
  }
}

At first, There was chaos

Lambda Expressions

To the rescue

public class AnonymousInnerClassTest {
  public static void main(String[] args) {
    new Thread(() -> 
        System.out.println("A thread created and running ..."))
            .start();
  }
}

"Let there be light"

and there was light

Method::signatures

let it divide the water from the water

public class Program {
  public static void main(String[] args) {
    new Thread(Program::printThreadCreatedMessage).start();
  }

  public static void printThreadCreatedMessage() {
    System.out.println("A thread created and running ...");
  }
}

The Dome

Divided the water under the dome from the water above

Old dog new tricks

@FunctionalInterface
public interface Runnable {
    void run();
}

Functional Interface

born to help the ignorant

If to speak the truth

Any interface with just one method unimplemented (do'nt forget there are default methods now) will work

Method reference > Lambda expressions

  • Lambda expressions tend to be code smell
  • Method reference express the what and not the how
  • Method reference encapsulates complexity

Instance Method

public class Program {
  public static void main(String[] args) {
    Arrays.asList("Saar", "Efes", ";)").stream()
        .map(String::length).forEach(System.out::println);
  }
}

of an arbitrary object of a particular type

Instance Method

public class Program {
  public static void main(String[] args) {
    Program program = new Program();

    new Thread(program::aRunnablePossibleMethod).start();
  }

  public void aRunnablePossibleMethod() {
    System.out.println("A thread created and running ...");
  }
}

of a particular object

Static Method

public class Program {
  public static void main(String[] args) {
    Arrays.asList("Saar", "Efes", ";)").stream()
        .map(Integer::valueOf).forEach(System.out::println);
  }
}

Constructor Reference

public class Program {
  public static void main(String[] args) {
    Integer willReturnOne = Optional.of(1)
            .orElseThrow(IllegalAccessException::new);
  }
}
public class AnonymousInnerClassTest {
  public static void main(String[] args) {
    Integer willReturnOne = Optional.of(1)
            .orElseThrow(() -> new IllegalAccessException());
  }
}

Same as

Optional<T>

When NullPointerExceptions are no longer fun

The Class

104 lines including imports

public final class Optional<T> {
	private final T value;

	public static <T> Optional<T> empty();
	public static <T> Optional<T> of(T arg);
	public static <T> Optional<T> ofNullable(T arg);

    	public               T             get();
	public            boolean          isPresent();
	public             void            ifPresent(Consumer<? super T> arg0);
	public          Optional<T>        filter(Predicate<? super T> arg0);
	public   <U>    Optional<U>        map(Function<? super T, ? extends U> arg0);
    	public   <U>    Optional<U>        flatMap(Function<? super T, Optional<U>> arg0);
	public               T             orElse(T arg0);
	public               T             orElseGet(Supplier<? extends T> arg0);
	public <X extends Throwable> T     orElseThrow(Supplier<? extends X> arg0) throws X;
}

Optional

What is it good for

Clarifies Intent
Removes uncertainty
No more null checks

Clarifies Intent

Any reader or consumer of an API will be beaten over the head with the fact that there might be nothing there and that a check is necessary before accessing the value.

public class Program {
  private static MeshekService meshekService;

  public static void main(String[] args) {
    Optional<Meshek> aMeshek = meshekService.findOne(meshekId);

    return aMeshek.orElseThrow(WrongIdGivenException::new);
  }
}

Removes Uncertainty

Without Optional the meaning of a null occurrence is unclear:

  • Legal representation of a stateĀ 
  • Implementation error e.g. a missing field or failed initialization.

No more null checks

Now nothing can be null anymore. Whether with annotations, assertions or plain checks, you never have to think about whether this argument or that return type can be null. It can't!

What gives

String version = "UNKNOWN";
if(computer != null){
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null){
    USB usb = soundcard.getUSB();
    if(usb != null){
      version = usb.getVersion();
    }
  }
}
String usbVersion = computer
            .flatMap(Computer::getSoundcard)
            .flatMap(Soundcard::getUSB)
            .map(USB::getVersion)
            .orElse("UNKNOWN");
String usbVersion = 
    computer?.getSoundcard()?.getUSB()?.getVersion() ?: "UNKNOWN";

C#

C#

C#

C#

C#

C#

C#

C#

C#

C#

C#

C#

Arrays.asList("a1", "a2", "b1", "c2", "c1")
    .stream()
    .filter(s -> s.startsWith("c"))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);


// Prints: 

// C1
// C2

Represents a sequence of elements and supports different kind of operations to perform computations upon those elements.

Intermediate

  • map
  • filter
  • sorted
  • mapToInt

Terminal

  • forEach
  • collect
  • findAny
  • anyMatch

Stream Operations

Processing Order

Stream.of("d2", "a2", "b1", "b3", "c")
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .anyMatch(s -> {
        System.out.println("anyMatch: " + s);
        return s.startsWith("A");
    });


// Prints:

// map:      d2
// anyMatch: D2
// map:      a2
// anyMatch: A2
Stream.of("d2", "a2", "b1", "b3", "c")
    .sorted((s1, s2) -> {
        System.out.printf("sort: %s; %s\n", s1, s2);
        return s1.compareTo(s2);
    })
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("a");
    })
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .forEach(s -> System.out.println("forEach: " + s));
// sort:    a2; d2
// sort:    b1; a2
// sort:    b1; d2
// sort:    b1; a2
// sort:    b3; b1
// sort:    b3; d2
// sort:    c; b3
// sort:    c; d2
// filter:  a2
// map:     a2
// forEach: A2
// filter:  b1
// filter:  b3
// filter:  c
// filter:  d2

Primitive Streams

DoubleStream
IntStream
LongStream

Primitives Operations

  • min
  • max
  • average
  • and more...

stream.mapToInt

Parallel Stream

Streams can be executed in parallel to increase runtime performance on large amount of input elements. Parallel streams use a common ForkJoinPool...

Arrays.asList("a1", "a2", "b1", "c2", "c1")
  .parallelStream()
  .filter(s -> {
    System.out.format("filter: %s [%s]\n",
      s, Thread.currentThread().getName());
    return true;
  })
  .map(s -> {
    System.out.format("map: %s [%s]\n",
      s, Thread.currentThread().getName());
    return s.toUpperCase();
  })
  .forEach(s -> System.out.format("forEach: %s [%s]\n",
    s, Thread.currentThread().getName()));
filter:  b1 [main]
filter:  a2 [ForkJoinPool.commonPool-worker-1]
map:     a2 [ForkJoinPool.commonPool-worker-1]
filter:  c2 [ForkJoinPool.commonPool-worker-3]
map:     c2 [ForkJoinPool.commonPool-worker-3]
filter:  c1 [ForkJoinPool.commonPool-worker-2]
map:     c1 [ForkJoinPool.commonPool-worker-2]
forEach: C2 [ForkJoinPool.commonPool-worker-3]
forEach: A2 [ForkJoinPool.commonPool-worker-1]
map:     b1 [main]
forEach: B1 [main]
filter:  a1 [ForkJoinPool.commonPool-worker-3]
map:     a1 [ForkJoinPool.commonPool-worker-3]
forEach: A1 [ForkJoinPool.commonPool-worker-3]
forEach: C1 [ForkJoinPool.commonPool-worker-2]
IntSummaryStatistics ageSummary =
    persons
        .stream()
        .collect(Collectors.summarizingInt(p -> p.age));

System.out.println(ageSummary);


// Prints:

// IntSummaryStatistics{count=4, sum=76, min=12,
//                        average=19.000000, max=23}
Map<Integer, List<Person>> personsByAge = persons
    .stream()
    .collect(Collectors.groupingBy(p -> p.age));

personsByAge
    .forEach((age, p) ->
       System.out.format("age %s: %s\n", age, p));


// Prints:

// age 18: [Max]
// age 23: [Peter, Pamela]
// age 12: [David]
String phrase = persons
    .stream()
    .filter(p -> p.age >= 18)
    .map(p -> p.name)
    .collect(Collectors.joining(" and ", "In Germany ", 
                                   " are of legal age."));

System.out.println(phrase);


// Prints:

// In Germany Max and Peter and Pamela are of legal age.
String phrase = persons
    .stream()
    .filter(p -> p.age >= 18)
    .map(p -> p.name)
    .collect(Collectors.joining(" and ", "In Germany ", 
                                   " are of legal age."));

System.out.println(phrase);


// Prints:

// In Germany Max and Peter and Pamela are of legal age.
Collector<Person, StringJoiner, String> personNameCollector =
    Collector.of(
        () -> new StringJoiner(" | "),          // supplier
        (j, p) -> j.add(p.name.toUpperCase()),  // accumulator
        (j1, j2) -> j1.merge(j2),               // combiner
        StringJoiner::toString);                // finisher

String names = persons
    .stream()
    .collect(personNameCollector);

System.out.println(names);  // MAX | PETER | PAMELA | DAVID

Questions?

FunctionalOptionalStream

By Boaz Berman

FunctionalOptionalStream

Java 8 features: FunctionalInterface, Optional container & Stream.

  • 147