6 Things you need to know before using Destructuring in Kotlin.

android Jul 28, 2020

too much de-structuring sweetness will hurt your tooth!

Destructuring is an easier way take out data from your objects. Basically

//class
data class Person(val name:String,val age:Int)

//instantiate
val person = Person("chetan", 25)

//access
val (name,age) = developer
de-structuring example

So, now you know another way to initial you variables ! Wow hurray! TheEnd !..

Well wait no! Don't use it just Yet. Destructuring thou comes in handy its has its own quirks and limitations lets go through them one by one.

Gotcha #1 : Missing component 🕵🏻‍♂️

 data class Items(val one: String, val two: String)

val items= Items("1","2")
val (one, two, three) = items
// boom! compile time error, type Items must have a 'component3()' function
gotcha 1 de-structuring

if your familiar with Javascript you would have guessed the code should have worked, we would have got third as “null”, well in case of Kotlin something equivalent like “Unit”, “Nothing” or “String?” etc but here we get an error of “component3()” function? what is that?

Under the Hood! ⚙️

In Javascript, destructuring comes in two forms:

  • Positional (for arrays and iterables)
  • Name/Structural (for objects).

In Kotlin, things are strongly-typed and thus unlike Javascript, which uses key-value/map-like encapsulation, there are full-encapsulation rules for objects. Hence accessing the object by name isn’t applicable unless your using Reflection which is slow and inefficient. Consequently, to support de-structuring, Kotlin team opted for limited use to only positional de-structuring for all use-case.

//example
data class Items(val one: String, val two: String)

val items= Items("1","2")
val (one, two) = items 

//converts to 
val one = items.component1
val one = items.component2

// means a getter function/operator/field is already present in those 
// class which automatically popluates these fields

// What the creators of Kotlin did was actually implement 
// 5 extension functions on many of the classes in the
// Collections.kt library that we commonly use.

operator fun <T> List<T>.component1(): T
operator fun <T> List<T>.component2(): T
operator fun <T> List<T>.component3(): T
operator fun <T> List<T>.component4(): T
operator fun <T> List<T>.component5(): T
destructing under the hood 

Gotcha #2 : how to do it a non-data class? 🧐

So the second problem is related to the first one itself these extensions only exist in the data class, so in case of normal class you need to create these function yourself.

//non data class destructuring 

class Items(val one:Int,val two:Int,val three:Int){
         ...
}

val (o,t,th)= item // create member function component1()...and so on

// manually declare componentN in class 
class Items(val one:Int,val two:Int,val three:Int){
         operator fun component1(): Int = one
         operator fun component2(): Int = two
         operator fun component3(): Int = three     
}

val (o,t,th)= item // no error
destructing non-data class

Gotcha #3 : Unpack All  📂

If we require second member of the object we still need to destructure all members object or atleast to the position we require

data class Items(val one:Int, val two:Int, val three:Int)

val items = Items(1,2,3)

val (no1, required, no2)= items 
// even if we require "required" paramter we still need to destructure all

//best practice for destructure of unused variables
val (_,requiredOnlyThis,_)= items
positional unpacking

Gotcha #4 : Reusing/mutable variables 🔃

you can declare them "var" for reusability but cannot use them again for destructuring.

var (one,two)= Items(1,2,3)
// one = 1
// two = 2

one = 5
// one = 5

(one,two)= Items(5,6,3) // error 
var not variable anymore!

Gotcha #5 : No support for default values 🤲

default values not supported for default params while unpacking.

var (one,two = 5)= Items(1,2,3) // complie time error
no support default Params

Gotcha #6: Major Deal Breaker ❌

Using positional data for representing a class, is not a good way for accessing its parameter, it doesn't provide any type/name check method to Android Studio/Lint to figure out if any refactoring is been done on the class and the points of destructing are also required to be refactored! i.e Ordering is part of the API contract! if you change your data class constructor arg reordering it will break.

data class Author(
	val name: String, 
    val email: String, 
    val blog:String
)

val chetan = Author(
	"ch8n",
    "dev.ch8n@gamil.com",
    "ch8n2@twitter"
)

//destructing
val (name,email,twitter) = chetan

//Later due to some reason

data class ContactMe(
	val email: String,
    val name: String, // rearranged params
    val twitter: String
)

val (name,email,twitter) = chetan 
// it will compile but now values of name and email is swapped
Runtime error of destructing

That's all fokes 🐰🥕 ! See ya again in next post .. Happy Hacking 💻 .

Update#1 : now you know the limitations positional destructing possess, a valid question to ask is when? and where? should we use destructing, those details and a sneaky little trick that I figured out to overcome positional refactoring issues are shared in the post below. see you on the other side👽.

AndroidBites | Kotlin Destructuring safely
positional destructuring |kotlin multiple assignment | kotlin destructuring component | destructuring declarations in kotlin | best practice for destructuring | who to destructure loops | destructure collection kotlin


Chetan gupta

Hi there! call me Ch8n, I'm a mobile technology enthusiast! love Android #kotlinAlltheWay, CodingMantra: #cleanCoder #TDD #SOLID #designpatterns

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.