Interoperability between Java and Javaslang

We distinguish between functions, values (sum-types) and tuples (product-types). Every type in Javaslang that represents a value is of type Value.

The Nature of Values

Values need a human readable string representation and have proper equals() and hashCode() implementations that consider the content, not the memory reference.

But there is more in common to all Value types.

Value creation. The idiomatic way to create values is to add one static import:

import static javaslang.API.*;

It allows us to instantiate values in a concise way, without using the keyword new.

// = Some(1)
Option(1)

// = List(1, 2, 3)
List(1, 2, 3)

It integrates well with pattern matching:

// = "one"
Match(Option(1)).of(
    Case(Some(1), "one"),
    Case(None(), "none")
)

Beyond that all Value types have manifold static factory methods, for example:

// = Try<Void>
Try.run(() -> { /*side effects*/ });

// = List(0, 1, 2)
List.range(0, 3);

Our Value types are persistent. This means that existing objects are unmodifiable and transformations create new instances (see Cats & Dogs).

var list = List(1, 2, 3);
var newList = list.prepend(0);

// List(1, 2, 3)
println(list);

// List(0, 1, 2, 3)
println(newList);

*) We use the future Java syntax for readability reasons.

Value structure. We made the most basic characteristics of values explicit, for example we distinguish between single-valued types and multi-valued types.

// = true (can hold at most one value)
Option(1).isSingleValued();

// = false (we talk about the type, not the instance)
List(1).isSingleValued();

Several common methods operate on one Value:

Option(1).get();            // = 1
Option(null).isEmpty();     // = true
Option(null).getOrElse(-1); // = -1

List(1, 2, 3).get();  // = 1
List().isEmpty();     // = true
List().getOrElse(-1); // = -1

Equality checks of values take the type and the contained elements into account:

// = true
List(1, 2, 3).equals(List(1, 2, 3));

// = false
List(1, 2, 3).equals(Stream(1, 2, 3));

Furthermore we are able to test (deep) structural equality, considering only order and equality of contained elements, regardless of the container:

// = true, because [1, 2, 3] == [1, 2, 3]
List(1, 2, 3).eq(Stream(1, 2, 3));

// = true, because [1] == [1]
List(1).eq(Option(1));

// = false, because [1] != []
List(1).eq(None());

// = true, because [[1]] == [[1]]
Set(Option(1)).eq(List(Future(1)))

All value types implement Iterable. This allows us to use the values in a for() loop:

// iterates over 1
for (int i : Option(1)) ;

// iterates over 1, 2, 3
for (int i : List(1, 2, 3)) ;

We relaxed the (semi-)monadic flatMap() of our collections in the way that the mapper function returns an Iterable instead of a specific Traversable:

interface Traverable<T> extends Value<T> {
    <U> Traversable<U> flatMap(Function<T, Iterable<U>> mapper);
}

Examples:

// List(1)
List(None(), Some(1)).flatMap(x -> x);

// = Set(1, 2)
Set(
    Collections.emptyList(),
    Collections.singletonList(1),
    Arrays.asList(1, 2)
).flatMap(x -> x);

// same using Javaslang's List
Set(List(), List(1), List(1, 2)).flatMap(x -> x);

Finally: all value types are Serializable. It is important to be able to send values over the wire!

Interoperability

We intentionally named the Javaslang collections similar to Scala's immutable collections. More specifically the javaslang.collection package contains common interfaces:

  • Traversable ← Seq, Set, Map, Multimap
  • Seq ← LinearSeq and IndexedSeq
  • Set ← SortedSet
  • Map ← SortedMap
  • Multimap ← SortedMultimap (will be added with 2.1.0-beta)

The implementations are:

  • Traversable ← PriorityQueue, Tree, Iterator (will change with 3.0.0)
  • IndexedSeq ← Array, CharSeq, Vector
  • LinearSeq ← List (+ Stack), Queue, Stream
  • Set ← HashSet, LinkedHashSet
  • SortedSet ← TreeSet, BitSet
  • Map ← HashMap, LinkedHashMap
  • SortedMap ← TreeMap
  • Multimap ← HashMultimap, LinkedHashMultimap
  • SortedMultimap ← TreeMultimap

The idiomatic way to circumvent name clashes with existing Java standard collections is:

  • fully qualify java.util collections
  • make use API methods to construct Javaslang collections

Conversion of Java collections to Javaslang collections. The base type of Javaslang collections is Traversable. Each collection has a static factory method ofAll() that takes an Iterable:

java.util.List<Integer> javaList = Arrays.asList(1, 2, 3);

// = List(1, 2, 3)
List.ofAll(javaList);

We added also ofAll() methods that directly take a java.util.stream.Stream:

java.util.stream.Stream<Integer> javaStream = javaList.stream();

// = Set(1, 2, 3)
Set.ofAll(javaStream);

Conversion of Javaslang values to Java types. Each Javaslang value has various Java conversion methods:

  • toJavaArray()
  • toJavaArray(Class)
  • toJavaCollection(Function)
  • toJavaList()
  • toJavaList(Function)
  • toJavaMap(Function)
  • toJavaMap(Supplier, Function)
  • toJavaMap(Supplier, Function, Function)
  • toJavaOptional()
  • toJavaParallelStream()
  • toJavaSet()
  • toJavaSet(Function)
  • toJavaStream()

A full list can be found here.

Examples:

// = [1, 2, 3]
Integer[] array = List(1, 2, 3).toJavaArray(Integer.class);

// = [1]
java.util.List<Integer> list = Option(1).toJavaList();

We added collector() methods that are intended to be used with Java streams:

// = List(1, 2, 3)
Arrays.asList(1, 2, 3)
      .stream()
      .collect(List.collector());

Java collection views. The upcoming 2.1.0-beta will contain methods that create Java collection views. The benefit over conversion methods is that it takes constant time instead of linear time. The tradeoff is that operations on views have the performance characteristics of the underlying Javaslang collection.

We will provide immutable and mutable views:

// = [1, 2, 3]
java.util.List<Integer> list = List(1, 2, 3).asJava();

// = [1, 2, 3]
java.util.Set<Integer> list = Set(1, 2, 3).asJavaMutable();

Additionally we will make it possible to act on a Java collection within a fluent Javaslang pipeline:

// = List(2, 4)
List(1, 2, 3).asJavaMutable(javaList -> {
    javaList.add(4);
}).filter(i -> i % 2 == 0);

// = HashMap((1, A), (2, B))
Map(1, "a", 2, "b").asJava(javaMap -> {
    /* read that j.u.Map */
}).mapValues(String::toUpperCase);

Of course the resulting Javaslang collection is still persistent/immutable!

Conversion between Javaslang values. A value type can be converted to almost all other value types. Some examples are:

// = List(1)
Some(1).toList();

// = None
List().toOption();

// = Set(1, 2)
List(1, 1, 2, 2).toSet();

This is the List of conversion methods:

  • toArray()
  • toCharSeq()
  • toEither(Object)
  • toEither(Supplier)
  • toInvalid(Object)
  • toInvalid(Supplier)
  • toLeft(Object)
  • toLeft(Supplier)
  • toLinkedMap(Function)
  • toLinkedMap(Function, Function)
  • toLinkedSet()
  • toList()
  • toMap(Function)
  • toMap(Function, Function)
  • toOption()
  • toPriorityQueue()
  • toPriorityQueue(Comparator)
  • toQueue()
  • toRight(Object)
  • toRight(Supplier)
  • toSet()
  • toSortedMap(Comparator, Function)
  • toSortedMap(Comparator, Function, Function)
  • toSortedMap(Function)
  • toSortedMap(Function, Function)
  • toSortedSet()
  • toSortedSet(Comparator)
  • toStack()
  • toStream()
  • toString()
  • toTree()
  • toTry()
  • toTry(Supplier)
  • toValid(Object)
  • toValid(Supplier)
  • toValidation(Object)
  • toValidation(Supplier)
  • toVector()

I hope this helps to understand the basic concept of values in Javaslang.

Have fun!

- Daniel