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.