5 Essentials for a Java 8 Tech Stack

An example Java 8 Tech Stack

1. Immutable Objects / Data Structures

Java doesn’t promote immutability by default, and creating immutable objects is a little more complex than it needs to be.

If in a multi-core highly concurrent world, “Mutable stateful objects are the new spaghetti code”, then annotation processors such as Lombok (@value, @builder, @wither) and Immutables (dedicated immutability) may be the solution.

Lombok

@Value
public class ValueObject {
String name;
List<Integer> counts;
Optional<String> description;
}

Immutables

@Value.Immutable
public interface ValueObject {
String name();
List<Integer> counts();
Optional<String> description();
}

2. Immutable / Persistent Collections

Unfortunately the legacy Java 8 Collections API is entirely mutable. Libraries such as Google’s Guava provide a set of Immutable Collections, collections that cannot be changed after their initialisation. Better yet, in many cases, are collections that allow data to be efficiently evolved. Taking their lead from a 1996 paper by Chris Okasaki.

One such standalone set of implementations is the very accessible pCollections library, from Groovy compiler lead Jochen Theodorou.

 PStack stack = ConsPStack.singleton(x);
 PStack stack2 = stack.plus(y);

cyclops-react Collection eXtensions, extends pCollections (as well as JDK collections) to offer a very rich, scala-like collection API.

3. Functional Types and Control Structures

Java 8 introduced some new functional classes such as Stream, Optional, CompletableFuture (and a new immutable DateTime api), but is missing many idioms from more advanced functional environments such as Scala, and certainly does not facilitate programming in a purely funcitonal manner the way Scalaz or Cats in the Scala world might. Enter Javaslang, which provides more powerful, yet accessible, functional idioms, and FunctionalJava which brings the possibility of totaly pure functional programming in Java much closer.

javaslang

List.of(1, 2, 3).sum();

FunctionalJava

arrayShow(intShow).println(array(1, 2, 3).map(i -> i + 42));

4. Cross Library Interoperability

Unfortunately for us the JDK does not provide a standard set of interfaces that would ensure interoperability across the new functional & functional style libraries that are emerging. In fact language choices around the power / complexity trade off, particularly with respect to Generics may prohibit it.

Cyclops provides a number of interoperability mechanisms, including generic type safe For Comprehensions that can ‘comprehend’ — or be taught to comprehend, any monad type (Stream, Optional, Either, CompletableFuture, Try, Reader, Writer etc) from any library. As well as similar wrappers for Monads & Functors and other types.

CompletableFuture<String> future = supplyAsync(this::loadData);



AnyM<List<String>> results = Do.add(future)
.addStream(()->Stream.of(“1”,”2))
.yield(ftr -> stm -> ftr+“:”+stm );

5. Better Streaming

Java 8 Stream provides a limited API for Streams or Sequences, primarily because of a focus in ensuring parallel Streams behave appropriately. The jOOλ library offers Streaming with much more functionality available via it’s Seq API (which extends the JDK 8 Stream interface) and it’s large number of custom operators.

Similarly if you need to take advantage of multiple cores, and would like a richer API than the JDK 8 Streams API, simple-react implements jOOλ Seq (and by extension JDK 8 Stream), but has a particular focus on concurrent / parallel asynchronous operations.

jOOλ

Seq.seq(lazyFileStream)              // Seq<String>
.zipWithIndex() // Seq<Tuple2<String, Long>>
.groupBy(tuple -> tuple.v2 / 500) // Map<Long, List<String>>
.forEach((index, batch) -> {
process(batch);
});

simple-react

LazyFutureStream.parallelCommonBuilder()
.react(data)
.batchBySize(BATCH_SIZE)
.map(this::process)
.run();