Everything you need to know about Kotlin 2.0

Bharat Kumar
4 min readJun 6, 2024
Took from Kotlin

In this article, we will dive into some of the most exciting features and changes that Kotlin 2.0 offers. Whether you’re a seasoned Kotlin developer or just starting, these updates will improve your coding experience.

Frontend Immediate Representation

One of the significant enhancements in Kotlin 2.0 is the improved handling of operators, especially when dealing with different numeric types like Long and Int. In previous versions, using the += operator with mismatched types could cause errors. For instance:

fun increment(list: MutableList<Long>) {
list[0] += 1 // Error in Kotlin 1.x because 1L is required
}

In Kotlin 2.0, this issue is resolved, making the code more intuitive and less error-prone:

fun increment(list: MutableList<Long>) {
list[0] += 1 // No Error in Kotlin 2.0
}

The Nullable Left side can’t use operator assignments event with the correct type due to nullability in 1.x but it works correctly with Kotlin 2.0

data class Section(
val students: MutableList<Long> = mutableListOf(),
)
fun incrememnt(section:Section?){
section?.students[0] += 1L // Error In 1.x but OK in 2.x
}

// In Kotlin 2.0
// Desugared Code :
// section.run{section?.run{students.set(0,students.get(0).plus(1) ) }

This enhancement simplifies the code and eliminates the need for explicit type conversions in many scenarios.

Enhanced Smart Casts

Kotlin has always been known for its smart casting capabilities, but Kotlin 2.0 takes it a step further. In Kotlin 1.x, smart casting didn’t carry over through variables. For example:

class Dog {
fun bark() {
println("bark")
}
}

fun petAnimal(animal: Any) {
val isDog = animal is Dog
if (isDog) {
// Error: animal is not cast to Dog
animal.bark()
}
}

With Kotlin 2.0, the smart cast works seamlessly, recognizing animal as Dog within the if block:

fun petAnimal(animal: Any) {
if (animal is Dog) {
animal.bark() // No error, animal is recognized as Dog
}
}

This enhancement makes the code more readable and reduces boilerplate type checks.

Introducing Guards

Kotlin 2.0 introduces a powerful feature called Guards, simplifying the use of conditions within when blocks. In Kotlin 1.x, combining multiple conditions within a when block was not possible. Kotlin 2.1 addresses this with a more concise syntax:

// Without Guards
fun render(status: Status): String =
when (status) {
Status.Loading -> "Loading"
is Status.OK && status.data.isEmpty() -> "No data" // Error
is Status.Error if status.isCritical -> "Critical problem" // Error
else -> "Unknown problem"
}

// With Guards in Kotlin 2.1
fun render(status: Status): String =
when (status) {
Status.Loading -> "Loading"
is Status.OK if status.data.isEmpty() -> "No data"
is Status.Error if status.isCritical -> "Critical problem"
else -> "Unknown problem"
}

It is still not there but will be there soon.

Extensible data Arguments

In composables such as LazyColunm’s, we have several overloads for the same function as we require it to have different parameters some parameters are mostly common but still they are there in all of them WHY?

They can make a data class but you would have to pass the values in the data class that would be ODD. Another drawback is that they had to write documentation that is mostly repetitive for every overload.

So they proposed dataargs so that they can keep the common aargs in the dataargs class and while we use the LazyColunm you don’t have to use the class in the calling point we could use it as it is now

$ Escaping Problem

One common issue in Kotlin is handling strings that include the $ symbol, which is normally interpreted as the start of a variable reference. While single-line strings can use a backslash to escape the $, this doesn't work for multi-line strings. Kotlin 2.0 introduces a new approach to address this:

val name = "Bharat"

"I'm $name" // Outputs: I'm Bharat

$$"I'm $name" // Outputs: I'm $name

By using $$ before a string, Kotlin treats $ as a literal character, allowing you to include it in your strings without escaping issues.

Name-Based Destruction Changes

Kotlin 2.0 will remove arbitrary name-based destruction to avoid confusion. Instead, it encourages more explicit and clear code practices.

data class User(val id: Int, val name: String)
fun doIt() {
val (userId, fullName) = User(1, "John")
// Ok in 1.x but will be an error in 2.x
}

Non-local Break/Continue

Handling loops in Kotlin with higher-order functions like forEach currently prohibits using break and continue to control flow. Kotlin 2.1 Beta changes this by allowing non-local break and continue, making inline functions more transparent and intuitive:

val list = listOf(1, 2, 3, 4, 5)
list.forEach { i ->
when {
i == 0 -> continue // Currently prohibited.
i == 1 -> break // Currently prohibited.
else -> println(i)
}
}

The Best Feature Explicit Backing Fields

One of the most anticipated features introduced at KotlinConf’23 is explicit backing fields. This feature allows you to define a backing field for properties directly, streamlining state management within classes

Without Explicit Backing Fields

class LoginViewModel() : ViewModel() {
private val _uiState = MutableStateFlow<LoginStates>()
val uiState: StateFlow<LoginStates> get() = _uiState
}

With Explicit Backing Fields

class LoginViewModel() : ViewModel() {
val uiState: StateFlow<LoginStates>
field = MutableStateFlow<LoginStates>()
}

Explicit backing fields make the code cleaner and the relationship between the public and private aspects of a property more explicit.

Kotlin 2.0 continues to push the boundaries of what’s possible in modern programming. From handling complex string scenarios to improving loop control and introducing explicit backing fields, these new features enhance Kotlin’s usability and power. Stay tuned as Kotlin 2.x continues to evolve and bring even more capabilities to your development toolkit.

If you found this article helpful, don’t forget to like, share, and leave your thoughts in the comments below!

--

--