Blog Infos
Author
Published
Topics
,
Published

Kotlin provides a handy mechanism for representing two pieces of data in a single value: . There is also for representing three pieces of data. I want to provide a simple guideline for when to use a Pair or Triple:

“Don’t ever, for any reason, use a Pair or Triple, for any reason, ever, no matter what, no matter where, or who you are with, or where you are going, or where you’ve been. Ever, for any reason. Whatsoever.”
— Michael Scott
— Matt Robertson

If you are unfamiliar with it, that was a reference to the sitcom The Office. On a more serious note, and are simply lazy data classes. And not the good kind of . To give a simple example, suppose we have a function that returns a size (small, medium, large) and a roast (light, medium, dark). With a Pair our function would look like this:

fun getCoffee(): Pair<Roast, Size>

and would be used like this

val coffee = getCoffee()
print(“A ${coffee.second}, ${coffee.first} roast coffee.")
view raw simple-pair.kt hosted with ❤ by GitHub

Now let’s suppose we also need to allow an option to “leave room for cream.” We’ll represent this as a Boolean. Now we have

fun getCoffee(): Triple<Roast, Size, Boolean>
...
val coffee = getCoffee()
print("A ${coffee.second}, ${coffee.first} roast coffee with ${if (coffee.third) "" else "no "}room for cream.")

Even an example as simple as this can benefit from a data class. Consider the following.

data class Coffee(
val roast: Roast,
val size: Size,
val leaveRoom: Boolean
)
...
fun getCoffee(): Coffee
...
val coffee = getCoffee()
print("A ${coffee.size}, ${coffee.roast} roast coffee with ${if (coffee.leaveRoom) "" else "no "}room for cream.")

At the cost of a very simple data class, the code is much more readable. And this is a simplified scenario where we already knew what was happening. Imagine running across this in a codebase:

val ounces = coffee.second.ounces - if (coffee.third) 2 else 0

compared to this

val ounces = coffee.size.ounces - if (coffee.leaveRoom) 2 else 0

Job Offers

Job Offers


    Android Developer

    Yoti Ltd
    Anywhere
    • Full Time
    apply now

    Senior Android Engineer – Big Release Team

    Zalando SE
    Berlin
    • Full Time
    apply now

    Android Manager (all genders)

    Babbel
    Berlin
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

, ,

Automated migration of Android apps to Bazel build system

Migrating large projects that consist of hundreds or thousands of modules and being maintained by a large team, from Gradle to Bazel might be challenging. I would like to discuss the process of automation of…
Watch Video

Automated migration of Android apps to Bazel build system

Pavlo Stavytskyi
Software Engineer
Lyft

Automated migration of Android apps to Bazel build system

Pavlo Stavytskyi
Software Engineer
Lyft

Automated migration of Android apps to Bazel build system

Pavlo Stavytskyi
Software Engineer
Lyft

Jobs

The data class version is immediately readable. The version requires following the surrounding logic and probably digging into the internals of just to figure out what is meant by .

Let’s complicate it a bit further and suppose we have a list of coffee orders, each with an associated name for the order. Using the Pair and Triple method we could represent it as follows:

val orders = mutableListOf<Pair<String, Triple<Roast, Size, Boolean>>>()

And yes, I have actually seen this in real codebases. Let’s consider our data class approach. In addition to the data class above, we’ll also add an data class:

data class Order(
val name: String,
val coffee: Coffee
)
view raw order-class.kt hosted with ❤ by GitHub

Now we can rewrite the above as follows:

val orders = mutableListOf<Order>()

Consider how these two signatures read.

1. Initialize orders as a mutable list of a pair of a string and a triple of a roast, a size, and a boolean.

2. Initialize orders as a mutable list of orders.

This is what future engineers in the codebase will read when they come across your code. Uncle Bob points out in his book Clean Code, “We are authors. And one thing about authors is that they have readers. Indeed, authors are responsiblefor communicating well with their readers” (p13).

We have an obligation to write clean, readable, maintainable code. Pairs and Triples are anti-patterns that should be avoided in preference for data classes. communicates nothing about meaning or intent. communicates meaning and intent clearly. And if a name doesn’t communicate meaning or intent clearly (perhaps is a bit unclear at first) then data classes allow for KDoc comments to clarify intent, an option that we don’t have with Pair and Triple.

Pairs and Triples are anti-patterns that should be avoided in preference for data classes.

Uncle Bob also notes in Clean Code, “the ratio of time spend reading vs. writing is well over 10:1. We are constantly reading old code as part of the effort to write new code. Because this ratio is so high, we want the reading of code to be easy, even if it makes the writing harder” (p14). Time spent creating data classes is a tiny price to pay for the time that will be saved from not having to decipher intent from Pairs and Triples.

Remember: friends don’t let friends use Pairs.

This article was originally published on proandroiddev.com on February 23, 2021

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu