Passing correct value as a method argument in Kotlin
When we are calling a function, there may or may not be an argument that we need to pass. Let’s say we have a person object.
data class Person(val name: String, val height: Int, val weight: Int)
Then, suppose we are going to check if a person is overweight or not with a method
fun isOverweight(weight: Int): Boolean
we can then use that method by writing
val person = Person("John Doe", 170, 90)
val isOverweight = isOverweight(person.weight)
Nothing is definitely wrong by doing this. But as you can see, the method isOverweight
is accepting an Int
. That Int
can be anything. You can even pass the height of the person to that function and it will run just fine.
val isOverweight = isOverweight(person.height)
Now, that can be a problem for us. Luckily, in Kotlin, we can try to minimize that.
Kotlin Named Parameter / Named Argument
Let’s try to re-write the code above with named argument.
val person = Person(name = "John Doe", height = 170, weight = 90)
val isOverweight = isOverweight(weight = person.weight)
One can argue that it does not make much of a difference. But for me personally, by doing this, it increases the readability of my code and it decreases the probability of me passing height
to my isOverweight
method.
But, sure there must be a better way to address this issue.
Wrapper Class
We can try to wrap the value in a class so that it represents a more specific type. If we re-write the code by using a wrapper class, it will looked like this.
data class Weight(val value: Int)
data class Height(val value: Int)val person = Person("John Doe", Height(170), Weight(90))
val isOverweight = isOverweight(person.weight)fun isOverweight(weight: Weight): Boolean
By using this approach, we can ensure that we will not be able to pass person.height
as the argument to isOverweight
function. This solution is really neat and this is definitely what we need.
But then, one may think that it isn’t worth creating a class to wrap a single primitive value just so we can avoid passing wrong value. Moreover, it introduces performance issue.
Since Kotlin 1.3, there is a way to do this without impacting the performance of our application.
Inline Class
Inline class is a way to wrap a value without introducing performance overhead. The way to use it is like this.
inline class Weight(val value: Int)
inline class Height(val value: Int)val person = Person("John Doe", Height(170), Weight(90))
val isOverweight = isOverweight(person.weight)fun isOverweight(weight: Weight): Boolean
It is basically the same code as the wrapper class approach, but we changed the data class
wrapper for height
and weight
to inline class
to remove the performance overhead.
Inline class is a wrapper that can only have a single member. That member is the value that you want to wrap.
When it is compiled, it will just be represented as the value it wrapped. You can see this Java bytecode for the Person class, for example.
public final class Person {
@NotNull
private final String name;
private final int height;
private final int weight; //...
}
You can see that the height and weight will be represented as regular JVM int
. This is why inline class
is different than a regular wrapper class. Although in some cases, it will still be represented as the wrapper type. You can read more on this page.
And since it is meant to be a wrapper class, there are limitations to what it can do versus the regular class.
Currently, it is still part of the experimental Kotlin feature.
To conclude, there are some ways to minimize the mistake of passing value that has the same type as argument. In Kotlin (since Kotlin 1.3), the best way is to use inline class
as it is truly made for that purpose. Despite that, there are some things that needs to be noted when using it. For more information, please look at the official documentation in Kotlin website.