Pairs and Triples in Kotlin (And Why You Shouldn't Use Them)
Kotlin’s sugar coating is great, but too much sugar can seriously hurt your health. In this blog post, we’ll discuss just that: why pairs and triples in Kotlin are an anti-pattern, and why you shouldn’t use them.
Using Pairs and Triples
Let’s say you have a form to collect the first and last name of a user, and you need to pass them to another screen. A convenient way would be to create a Pair
:
val user : Pair<String, String> = Pair("John", "Doe")
What if you want to collect and pass the user’s age as well? You could use a Triple
instead of a Pair
:
val user : Triple<String, String, Int> = Triple("John", "Doe", 20)
Simple, right? This is where the convenience ends, for several reasons. First, if you’re passing this to another screen, it’s easy to get confused about what these properties mean. Take a look at the following example:
if(user.third > 21) { setEligible(user.first, user.second) }
If you were to see this chunk of code without previous knowledge of how the Triple
was created, you could easily be confused with its meaning. What is user.third
and why should it be over 21? What are user.first
and user.second
, and why are they parameters for the setEligible()
function? Pairs and triples are easy to write, but they’re often confusing to read and later interpret. This is always a red flag that something should be changed in the code.
This is also where your expandability possibilities end — there’s no Quadruple
or anything of that sort. If you want to add another property, you’d have to create your own data class. Or if you’re too lazy, you could go with a more radical solution, such as Triple<Pair, Int, String>
(where the pair contains the first and last name), which would make things even more confusing:
if(user.second > 21) { setEligible(user.first.first, user.first.second) }
So if pairs and triples are problematic, why do we use them?
Pairs and Triples Under the Hood
We create pairs and triples because we want to avoid creating our own data classes. But let’s dig into the Tuples.kt
class, which provides the implementation of Pair
and Triple
, to see what they really are:
public data class Pair<out A, out B>( public val first: A, public val second: B ) (...) public data class Triple<out A, out B, out C>( public val first: A, public val second: B, public val third: C )
We avoid writing data classes and instead write… data classes?
Conclusion
Pair
and Triple
are just data classes that are (arguably) more convenient than making our own data class, but there’s not much sense in using them. Although there’s nothing wrong with how Pair
and Triple
work, they violate the O in SOLID, in that they’re not open for extension. Additionally, they confuse code reviewers, and they could possibly even confuse future-you. Yes, making a completely new data class does take more time, but it contributes to improved code readability.