In the previous article I talked about extension functions and all of the nice functionality they provide. But wait, there's more! This time we're going to talk about parameters. While that might seem like a boring topic, I promise it will be quite interesting.
Default values
In Kotlin, it is possible to assign default values to optional parameters to get Java's overload functionality for free. The following Java code
public void doStuff(int i) {
doStuff(i, true);
}
public void doStuff(int i, boolean b) {
}
is equivalent to this Kotlin code
// Note that in Kotlin, primitive types start with a capital
fun doStuff(i: Int, b: Boolean = true) {
}
So we saved having to type the lines of an entire function and obviously, the more optional parameters you have the greater the advantage will be. Besides, if you need to support a variety of different parameter combinations, you can save an exponential amount of lines.
Named Parameters
Have you ever dealt with a really cryptic API where it's impossible to tell what each parameter does from just looking at it? For instance JUnit's assertEquals
method, can you tell without looking it up which of the parameters of assertEquals(myVar, otherVar)
should be the expected value and which should be the actual value? Your IDE might have inline hints to show the names of the parameters, but this won't help you much when you view the code outside of the comfort of your IDE, for instance during a code review on GitHub.
With Kotlin we can use named parameters to be more explicit, assertEquals(expected = myVar, actual = otherVar)
Imagine we have some classes such as this:
// A data class is a final class for which Kotlin generates
// a default equals, hashCode, toString and copy function
// It is comparable to Lombok's @Data annotation
data class Person(
val name: Name,
val age: Int
)
data class Name(
val firstName: String,
val lastName: String
)
// Note that all of our properties are final since they are declared with val instead of var
How would you instantiate a Person?
Builders
If you've worked with Java data classes or, POJO's or beans as they're sometimes colloquially referred to, you know that instantiating them can be a hassle. Nobody likes to use Person myPerson = new Person(new Name("Jack", "John"), 18)
, because again, which of the 'name' parameters is the first and which is the last name? Is his name supposed to be Jack John or John Jack? Oftentimes in Java we resort to using Lombok's @Builder
annotation to create a builder for us. The issue with using builders is how to make sure all the required properties get set as well as any optional ones.
Kotlin eradicates the need for builders by being able to use named parameters and default parameters when constructing your data class:
val person = Person(
name = Name (
firstName = "Jack",
lastName = "John"
),
age = 18
)
Now doesn't that look nice? It's quite clear which property is set to which value just by looking at this code.
Ordering
Even though it's not very useful or advisable to do so, using named parameters allows you to mix up the parameter order, val name = Name(lastName = "John", firstName = "Jack")
is perfectly valid in our example. You'll notice we defined the order of them in our data class differently.
Bonus: Lambdas
Whenever a function takes a lambda as a parameter, such as
// This function takes a String as its first parameter
// and a lambda consuming that String and not returning anything as its second parameter
fun doStuff(s: String, block: (String) -> Unit) {
block.invoke()
}
We have the option of enclosing the lambda as a real parameter inside the parentheses:
doStuff("Hello World", { println(it) } )
or putting the lambda outside of the parentheses for better readability:
doStuff("Hello World") {
println(it)
}
Summary
We've looked at all the fun and flexible ways Kotlin deals with parameters. With default parameters, we don't have to specify a plethora of overloads any more and with named parameters, we can be more explicit and use data class constructors as proper builders. We've seen that the order can be changed and that we can put lambda parameters outside of the called function's parentheses to make them more readable. Stay tuned for the next article where we'll dive even deeper into the fun and wacky world of Kotlin.