Android Test-Driven Development by Tutorials,
Second Edition – Now Updated!

Build testable, sustainable Android apps via JUnit, Mockito, and Espresso
by diving into test-driven development (TDD) in this newly-updated book.

Home Android & Kotlin Tutorials

Destructuring Declarations in Kotlin

Destructuring declarations is a Kotlin feature that gives you a tool for easily extracting data from a collection in a simple and clean way.

5/5 1 Rating

Version

  • Kotlin 1.4, Other, IntelliJ IDEA

When programming in Kotlin, you work with a lot of data. Sometimes you gather data into a collection, such as an array, pair, data class or class. Of course, you need a way to extract data from these collections, that is Destructuring Declarations and you will learn about it in this tutorial.

There are many ways to extract data from a collection, such as using an index. With destructuring declarations, Kotlin gives you a tool for easily extracting data from a collection, no matter the size of it.

In this tutorial, you’ll build Destructuring Cryptocurrencies, a simple console app that highlights the use of destructuring declarations, while learning the names of different types of cryptocurrencies. You’ll learn about:

  • Destructuring declarations of a pair, array and map.
  • The use case of destructuring declarations in lambda.
  • The gotcha in destructuring declarations of a data class.
  • How to make a class support destructuring declarations.

Getting Started

Download the starter project by clicking Download Materials at the top or bottom of the tutorial. Open the starter project in IntelliJ IDEA.

If you’re not familiar with IntelliJ IDEA, please refer to the sample chapter, Getting started with IntelliJ IDEA, in our Kotlin book. You can also use Kotlin Playground to work on this tutorial.

Open DestructuringCryptocurrencies.kt. In that file, you’ll see some data, including a pair, array and data class. You’ll use destructuring declarations on these later.

Destructuring Declarations

First off, you may be wondering what does the term Destrucutring Declarations means. In simple words it is the process of creating multiple variables in a single line of code. It is a syntax feature of Kotlin that may be hard to understand at first but can come in handy and make your code look cleaner.

It is usually used in collections, or other data types, in this tutorial you will learn how to use them in:

  • Arrays
  • Pairs
  • Maps
  • Lambdas
  • Data classes

You may find other use-cases but this tutorial will give you the tools to apply it anywhere.

Destructuring Declarations of a Pair, Array and Map

The first and most common use of Destructuring Declarations is data structures. Here, you’ll use destructuring declarations with a pair, array and map. These are common data types you might want to extract elements from.

Destructuring Declarations of A Pair

First start with a Pair, which as you may know is a data structure that contains two elements.

To begin Build and run the sample project. Because this is a console app, you can check the app’s output in the run panel window. You’ll find some information:

The initial output of the destructuring declarations starter project

As you can see from the output above, you’ll use destructuring declarations on many kinds of data, such as pairs, arrays and data classes.

First, you’ll use destructuring declarations on a pair. When extracting information from a Pair, you can extract elements one at a time using the first and second properties, which is the most common way. But with destructuring declarations, you can extract both elements from the pair in one go.

Replace TODO 1 with:

val (stringFromPair1, stringFromPair2) = pair
println("$stringFromPair1, $stringFromPair2")

Build and run. You can see the value of stringFromPair1 and stringFromPair2.

The output of destructuring declarations of a pair

Destructuring declarations is similar to declaring a normal variable. You choose whether you want to use val or var. Then you enclose two variables separated by a comma with parentheses.

The code looks much simpler than the extraction sample shown above the code you just added.

You must use val or var for both variables. You can’t use val for one variable and var for other variable. In case you need to use val for one and var for another value, you can’t use destructuring declarations.

If you don’t use destructuring declarations, you’ll need to extract the values one by one using an index operator. Using destructuring declarations saves a couple of extra lines.

However, destructuring declarations don’t just save lines, it’s also more natural in some cases. For example, consider the pair as a data collection of coordinates in a 2D landscape. You can extract x and y one by one from a pair like this:

val x = coordinates[0]
val y = coordinates[1]

But it’s more natural to use destructuring declarations to extract them as a pair, like this:

val (x, y) = coordinates

It’s easier to read because you don’t have to read the index 0 and the index 1, which are superfluous.

Destructuring Declarations of An Array

Next, let’s go on with a more common data structure, that contains not just two values, but many. In the next example, you’ll use destructuring declarations on an array.

First, look at the limitations of destructuring an array and other ways to extract an element from an array. In the code, you will see two examples of how to obtain an element from an array, now imagine having to repeat this for each of the elements. Look at the code below to see which part of the code we are referring to:

The way of destructuring declarations of an array and a pair is the same. However, you can only use destructuring declarations on up to five elements in an array.

Let’s look at an example of this. Uncomment this line:

// val (string1, string2, string3, string4, string5, string6) = stringList

If you uncomment the commented line in the sample code above, you will notice there is an error in the sixth element. Now, the line will look like this:

val (string1, string2, string3, string4, string5, string6) = stringList

Build and run. As you can see, you get an error.

Error on extracting six elements from an array

The error is about missing component6.

Now undo the change. Comment the line again so the line becomes:

// val (string1, string2, string3, string4, string5, string6) = stringList

Build and run again, and the app should be back to normal.

Great job! You have already learned how to do Destructuring Declarations in two data types: Pairs and Arrays. Keep going to learn even more options.

Component1, Component2, …, ComponentN

The error in the previous example lets you know you can use similar methods with indexes below the one it is specifying, such as component5. Doing val componentString5 = stringList.component5() is one way to extract an element from an array. As you may have guessed, you’ll get the fifth element with component5. But as mentioned before you would need to repeat that line several times in order to extract all components.

Nevertheless, Kotlin’s designers thought developers would only need five elements at most when using Destructuring Declarations on an array. This limitation is present in similar data types, like lists.

Later in this tutorial, you’ll see how to add support to a class to extract more than five elements in destructuring declarations.

Another thing to note is that, you’d get a similar error if you uncommented this line val string6 = stringList.component6().

component1, component2 through componentN, where N represents the largest element you want to extract, are the core of destructuring declarations.

Destructuring declarations as syntactic sugar

Destructuring declarations is also one of those Kotlin language features, that are usually known as “syntactic sugar” for accessing component1, component2, …, componentN from an object.

An array or list only supports extracting up to five elements, because their classes only define component1 through component5. You can see the definition of component5 in this Kotlin documentation, but you won’t find the definition of component6.

Besides using a method like component5, you can also extract an element using a conventional method, such as an index, for example stringList[3].

Can you guess why the Kotlin designers set five as the maximum number of elements you can extract in destructuring declarations? If they didn’t put this limit, you could extract ten elements, like this:

val (tree, mountain, bridge, beach, castle, river, waterfall, forest, cave, tower, building, fortress) = words

The line above is more than a hundred characters in width. It’s not good to write code that’s too long because it’s harder to read. You could split the variables with new lines, but at that point, you might as well use the index operator to extract the values.

On top of that, it’s harder to recall several items. In the example above, cave is the ninth item from words. To make sure cave gets the correct value, you have to count from tree as the first element to cave as the ninth element and not make a mistake in the counting process.

You might as well use the index operator like this:

val tree = words[0]
val cave = words[8]

With all this in mind, the Kotlin designers set the maximum number of elements to five when using destructuring declarations with a collection data structure. If you extract more than five elements, you’re abusing the API.

But what if you only want the first element and the fifth element, and not the three elements between them? You’ll learn how to do that next.

Omitting Variables in Destructuring Declarations

You can omit the variables that you don’t want with an underscore. Using this approach, you avoid polluting namespace. Behind the scenes, Kotlin won’t call component2, component3 and component4.

Replace TODO 2 with the code below:

val (string1, _, _, _, string5) = stringList
println("$string1, $string5")

Build and run. You can see the values of string1 and string5.

The output of destructuring declarations of an array

Python inspired Kotlin in this case. In Python, underscore is a throwaway variable: you just want to ignore the values.

If you’re wondering why you couldn’t use different names for these throwaway variables, the answer is you could. But you have to use different names for more than one throwaway variable. For example, you could use variable names with one character like this:

val (string1, a, b, c, string5) = stringList
println("$string1, $string5")

In the code above, you ignore a, b and c. But somehow you have to store the extra information that there are three variables you have to ignore in the rest of the code. Using underscore is easier for your brain.

But what if you want to use the same name for each throwaway variable? For example, you want to use tmp as the name for every throwaway variable like this:

val (string1, tmp, tmp, tmp, string5) = stringList
println("$string1, $string5")

If you use the same name for more than one variable declaration, you’ll get conflicting declarations errors.

Good job! Now that you know how to use destructuring declarations, you’ll learn common use cases where you might want to use them.

Common Use Case of Destructuring Declarations

Destructuring declarations, although not very common in beginner code, can be helpful in a lot of situations and make your code better, in this section we will review common use-cases for them.

Replace TODO 3 with the following:

val (bitcoinFromFunction, btcFromFunction) = functionReturningPair()
println("$bitcoinFromFunction, $btcFromFunction")

Take a look at the function that is two lines above the code you just pasted fun functionReturningPair() : Pair = Pair("Bitcoin", "BTC"). The function is pretty straightforward, you are just returning a pair. In real life this kind of function would do some processing and then return the value, but for the example, it is very simple.

Build and run. You can see the value of bitcoinFromFunction and btcFromFunction.

The output of destructuring declarations of a function

Developers commonly use destructuring declarations when they want to return more than one value in a function and use them immediately after executing a function.

The alternative is collecting the return result with a collection data structure then later spliting it with the index operator or destructuring declarations. As shown in the code below, it’s not efficient:

val aPair = functionReturningPair()
val bitcoinFromFunction = aPair[0]
val btcFromFunction = aPair[1]
println("$bitcoinFromFunction, $btcFromFunction")

As you can see, the code is too verbose.

It’s common to return a collection from a function. For example, you may have a function that calculates the longitude and latitude of a location.

But outside the function, you may want to use the latitude and longitude separately instead of as a package. Destructuring declarations is handy in this situation.

Destructuring Declarations of A Map

Another data structure where you can use Destructuring Declarations is the iteration of a map. Maps are key-value pairs grouped in a collection. They are similar to an array, with the difference that you can name the key, instead of it being a sequential number.

Replace TODO 4 with this code:

for ((cryptoFullname, cryptoSymbol) in cryptoSymbolMap) {
    println("$cryptoFullname, $cryptoSymbol")
}

Build and run. You can see the key and value of every item in the map.

The output of destructuring declarations of a map

In the code above, cryptoSymbolMap is a map. You use destructuring declarations on the element while iterating the map by enclosing two variables separated by a comma in parentheses.

The first variable is the element’s key, while the second variable is the element’s value. It’s similar to the previous way of destructuring elements but you don’t use val or var.

When iterating a map with forEach, you use it the same way.

Destructuring Declarations in a Lambda Function

Lambda functions are very common in Kotlin code and in most modern programming languages, the syntax of Lambda functions is { a, b -> a + b }. In this section, we will look at how destructuring declarations can be useful in this kind of function.

Replace TODO 5 with:

cryptoSymbolMap.forEach { (cryptoFullname, cryptoSymbol) ->
    println("$cryptoFullname, $cryptoSymbol")
}

Build and run. You can see the key and value of every item in the map.

The output of destructuring declarations of a map with forEach

In the code above, forEach accepts a lambda that accepts a parameter. You use destructuring declarations on this parameter to extract them into two variables.

The type of the variable as the result of each iteration is Entry, which is a type that represents a key/value pair inside a Map. It has methods you’re familiar with such as: component1 and component2.

If you read the definition of component1, you’ll see the method returns the key component of this entry. Likewise, if you read the definition of component2, you’ll see the method returns the value component of this entry. There’s also a method to convert the entry to a pair.

Destructuring Declarations in Lambda

There’s a difference between a lambda that accepts two parameters and a lambda that accepts one parameter. You can use destructuring declarations inside the lambda that accepts one parameter.

Look at an example of a lambda with two parameters:

val lambdaCrypto = { name: String, symbol: String ->
    println("$name, $symbol")
}
lambdaCrypto("Bitcoin", "BTC")

In the lambda above, you call the lambda with two parameters: name and symbol, which are of the type String.

Now, look at an example of the same lambda with only one parameter. You use destructuring declarations on that parameter to extract them into two variables.

Replace TODO 6 with:

val lambdaCrypto2 = { (name: String, symbol: String): Parameter ->
    println("$name, $symbol")
}
lambdaCrypto2(param)

Build and run. You can see the values of name and symbol.

The output of destructuring declarations in lambda

In the code above, notice parentheses enclose two variables, while there are none in the lambda that accepts two variables.

If you come from a Python background, you may wonder whether you can destructure or unpack the arguments when calling the function as opposed to doing it inside the function like this:

lambdaCrypto(*Pair("Bitcoin", "BTC"))

There’s a spread operator in Kotlin. However, it’s not as flexible as the spread operator in Python. Because of that, you’ll get an error. You can only apply the spread operator in a vararg position.

The Gotcha in Destructuring A Data Class

One of the most common data types you’ll extract using destructuring declarations is a data class. However, you must be careful because the destructuring declarations of a data class are positional-based, not named-based.

Look at this data class instance and example of the wrong way to use destructuring declarations:

data class Founder(var name: String, var cryptocurrency: String, var country: String)
val founder = Founder("Vitalik", "Ethereum", "Canada")
// val (cryptocurrency, name, country) = founder

cryptocurrency has the value Vitalik, not Ethereum.

To prove this is the case, uncomment:

// val (cryptocurrency, name, country) = founder

Then add println to print the value. The line will look like this:

val (cryptocurrency, name, country) = founder
println(cryptocurrency)

Build and run. You can see the value of cryptocurrency.

The output of wrong value in destructuring declarations of a data class

As you can see, cryptocurrency indeed has the value Vitalik.

To have the correct values, you need to realize the destructuring declarations of a data class are positional-based.

Now, undo the last change or delete the lines you added before. The line you modified will look like this:

// val (cryptocurrency, name, country) = founder

Then replace TODO 7 with:

val (name, cryptocurrency, country) = founder
println("$name, $cryptocurrency, $country")

Build and run. You can see the value of name, cryptocurrency and country.

The output of destructuring declarations of a data class

Now, name, cryptocurrency and country refer to the proper values.

If you’re annoyed by the maximum of five elements in destructuring declarations of a collection, good news! The data class doesn’t have that limitation.

Even if your data class has twenty components, you can extract all of them with destructuring declarations.

However, you can’t provide an explicit implementation for the componentN in the data class. So, if you want to prank your colleagues by overriding component2 to return the third element and component3 to return the first element, you can forget it. Fortunately, the Kotlin designers put in restrictions to protect your colleagues from your cruel prank. :]

But what if you persist in your attempt to prank your colleagues? That’s where the custom class can help you.

Making A Class Support Destructuring Declarations

What if you want to add support for destructuring declarations of more than five elements on a class? Look at the definition of Cryptocurrencies:

class Cryptocurrencies {
    operator fun component1() : String = "Bitcoin"
    operator fun component2() : String = "Ethereum"
    operator fun component3() : String = "Binance Coin"
    operator fun component4() : String = "Tether"
    operator fun component5() : String = "XRP"
    operator fun component6() : String = "Uniswap"
}

Remember component5 which you used to extract the fifth element from an array? If you want to extract the sixth element from a class, define component6.

The method must be an operator method. If you want to extract twenty elements from a class, you need to define component1 to component20.

Replace TODO 8 with:

val (bitcoin, ethereum, binanceCoin, tether, xrp, uniswap) = Cryptocurrencies()
println("$bitcoin, $ethereum, $binanceCoin, $tether, $xrp, $uniswap")

Build and run.

The output of destructuring declarations of a custom class

As you can see now, you extracted six elements from the instance of a class.

Remember that you wanted to prank your colleagues? You can override component3 to return something annoying. For example, you could do something like this:

  //operator fun component3() : String = "Binance Coin"
  operator fun component3() : String {
      val current = LocalDateTime.now()
      if (current.monthValue % 2 == 0) {
          return "Binance Coin"
      } else {
          return "Doge"
      }
  }

If your colleagues use this class, they’ll get different data when they extract component3 depending on whether the current month is odd or even. Don’t forget to import java.time.LocalDateTime first if you’re serious with this prank.

Where to Go From Here?

You can download the complete project by clicking Download Materials at the top or bottom of this tutorial. In this tutorial you learned:

  • What are destructuring declarations and how to use them.
  • How to use destructuring declarations in collections: Pairs, Arrays and Maps.
  • Destructuring declarations in lambda functions.
  • The use of destructuring declarations in data classes and custom classes.

This project is only the beginning: there’s more to explore. Why not add support for destructuring declarations of more than five elements of an array or list? You can do it by writing operator methods.

You learned about the destructuring declarations in Kotlin. But you’ve only scratched the surface of the Kotlin programming language. If you want to know more about Kotlin, read our book Kotlin Apprentice, which has more info about Kotlin.

If you have any suggestions, questions or anything else, join the discussion below.

Average Rating

5/5

Add a rating for this content

1 rating

More like this

Contributors

Comments