Effective Kotlin: Lambdas and Streams

Let’s see how we can apply Joshua Bloch’s Effective Java in the Kotlin world. Today’s topic is Lambdas and Streams.

Item 42: Prefer lambdas to anonymous classes

As of Kotlin 1.3, SAM conversion is only supported when calling Java interfaces (because we can use higher-order functions), and support for Kotlin interfaces will be added in 1.4.

As a result of this and trailing lambda, we can often see DSL-style code like below:

// Java interface
public interface View.OnClickListener {
  void onClick(View v);
}

// Kotlin code
view.setOnClickListener {
  ...
}

Item 43: Prefer method references to lambdas

There is nothing special in Kotlin about this item.

Item 44: Favor the use of standard functional interfaces

In Kotlin, we prefer higher-order functions over interfaces. For example, the filter() extension function on collections accepts a function instead of an interface:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
  ...
}

Item 45: Use streams judiciously

If you can’t use Java’s streams API (e.g. developing Android), Kotlin provides sequences synchronous streams, and flows for asynchronous streams.

Item 46: Prefer side-effect-free functions in streams

Obviously, the paradigm of functional programming should be applied to sequences and flows in Kotlin.

Item 47: Prefer Collection to Stream as a return type

In Kotlin, we might want to return a sequence or flow instead, both of which provide lots of operators to use, and can be easily converted to collections. More importantly, both are using lazy evaluation, and may significantly improve performance.

Item 48: Use caution when making streams parallel

In Kotlin, sequences are designed for synchronous streams, flows are for asynchronous streams and they don’t support parallel processing. However, coroutines are designed to support parallelism.


See also