Features You Waited For So Long in Kotlin

Kotlin 2.4.20 gives your collections opinions, and 2.2’s compiler finally learns to read the room

Some Kotlin features get a lot of attention when they arrive. Others are much less noticeable, but still make everyday code a little easier to write. Two examples of the latter are allDistinctBy/ allEqualBy in Kotlin 2.4.20 and context-sensitive resolution, which was introduced experimentally in Kotlin 2.2.

allDistinctBy/ allEqualBy: A Simpler Way to Check Collections

For years, checking whether all values in a collection were unique often looked something like this:

val users = listOf(
User(name = "A", age = 20),
User(name = "B", age = 25),
User(name = "C", age = 30)
)

users.map { it.age }.distinct().size == users.size // true

It works, but it creates another list, removes duplicates, counts the result, and then compares the sizes just to get a Boolean answer.

Kotlin 2.4.20 adds a more direct way to express the same check:

users.allDistinctBy { it.age } // true
users.allEqualBy { it.age } // false

allDistinctBy returns false as soon as it finds a duplicate, so it doesn’t need to process the entire collection or create a separate Set. allEqualBy does the opposite: it checks whether all elements produce the same value when passed through the selector.

Both functions work with Iterable, Sequence, and arrays. They use structural equality. For floating-point values, they follow Double.equals semantics, so NaN is considered equal to NaN, while -0.0 is different from 0.0.

They’re still @ExperimentalStdlibApi, so you’ll need to opt in before using them in production code. The underlying allDistinct() and allEqual() requests have also been around in YouTrack for quite a while, which makes their appearance in the standard library feel long overdue.

Context-Sensitive Resolution: Less Repetition in when

The second feature is more about syntax. When the compiler already knows the type you’re working with, you can sometimes leave out the type qualifier.

Before:

enum class Problem { CONNECTION, AUTHENTICATION, DATABASE, UNKNOWN }

fun message(problem: Problem): String = when (problem) {
Problem.CONNECTION -> "connection"
Problem.AUTHENTICATION -> "authentication"
Problem.DATABASE -> "database"
Problem.UNKNOWN -> "unknown"
}

With context-sensitive resolution:

fun message(problem: Problem): String = when (problem) {
CONNECTION -> "connection"
AUTHENTICATION -> "authentication"
DATABASE -> "database"
UNKNOWN -> "unknown"
}

The compiler can infer Problem from the type expected in the when expression and resolve the unqualified names accordingly. This works with enum entries and other supported declarations, including members of sealed hierarchies and object declarations, in contexts where the required type information is available.

There is a catch, though. Context-sensitive resolution is still experimental. It was introduced experimentally in Kotlin 2.2 and requires -Xcontext-sensitive-resolution in the compiler options.

One of the reasons to be careful is ambiguity. If another declaration with the same name is brought into scope, the shorter form can become ambiguous. The compiler has added warnings for some of these cases, but it is still worth treating the feature as experimental rather than assuming the syntax is completely settled.

None of that makes the feature unusable. It just means that, for now, it is better suited to experimentation and projects where you’re comfortable adopting experimental Kotlin features.

Should You Actually Use These?

allDistinctBy / allEqualBy are fairly straightforward additions. If you already use experimental standard-library APIs, they are useful replacements for the more verbose collection patterns they address.

Context-sensitive resolution is worth trying in a side project or a codebase where experimental compiler features are acceptable. It can make enum-heavy and sealed-class-heavy code easier to read, particularly in code that contains a lot of when expressions.

Kotlin continues to remove small pieces of boilerplate from everyday code. Neither of these features changes the way we build applications, but both make some common Kotlin patterns a little cleaner.

And don’t tell me you didn’t want these features 😎


Features You Waited For So Long in Kotlin was originally published in ProAndroidDev on Medium, where people are continuing the conversation by highlighting and responding to this story.