- Constructor Default Values and Named Arguments
- Default values for constructor parameters
- When you don’t provide defaults for all parameters
- Named arguments
- Kotlin Constructors
- 1. Overview
- 2. Primary Constructor
- 2.1. Primary Constructors on the JVM
- 3. Initializing Properties from Constructor Parameters
- 4. Initializer Blocks
- 5. Secondary Constructor
- 6. Constructors and Inheritance
- 7. Conclusion
Constructor Default Values and Named Arguments
Kotlin has two nice features that you’ll also find in Scala:
- You can supply default values for constructor parameters
- You can use named arguments when calling a constructor
Default values for constructor parameters
A convenient Kotlin feature is that you can supply default values for constructor parameters. For example, you could define a Socket class like this:
class Socket(var timeout: Int, var linger: Int)
That’s nice, but you can make this class even better by supplying default values for the timeout and linger parameters:
class Socket(var timeout: Int = 2000, var linger: Int = 3000) < override fun toString(): String = "timeout: $, linger: $" >
By supplying default values for the parameters, you can now create a new Socket in a variety of different ways:
Socket() Socket(1000) Socket(4000, 6000)
This is what those examples look like in the REPL:
>>> Socket() timeout: 2000, linger: 3000 >>> Socket(1000) timeout: 1000, linger: 3000 >>> Socket(4000, 6000) timeout: 4000, linger: 6000
- When all parameters have default values, you don’t have to provide any values when creating a new instance
- If you supply one value, it’s used for the first named parameter
- You can override the default values with your own values
An important implication of this is that default values have the effect of letting consumers consumers create instances of your class in a variety of ways — in a sense they work just as though you had created multiple, different constructors for your class.
When you don’t provide defaults for all parameters
As a word of caution, it generally doesn’t make any sense to provide a default value for an early parameter without providing a default for subsequent parameters.
// don't do this class Socket(var timeout: Int = 5000, var linger: Int) < override fun toString(): String = "timeout: $, linger: $" >
If you do that, you’ll get errors when trying to construct an instance with zero or one parameters:
>>> val s = Socket() error: no value passed for parameter 'linger' val s = Socket() ^ >>> val s = Socket(2) error: no value passed for parameter 'linger' val s = Socket(2) ^
If you’re not going to provide default values for all parameters, you should only provide default values for the last parameters in the constructor:
// this is a little better class Socket(var timeout: Int, var linger: Int = 5000) < override fun toString(): String = "timeout: $, linger: $" >
With this approach the zero-argument constructor still fails, but you can use the one-argument constructor:
>>> val s = Socket() error: no value passed for parameter 'timeout' val s = Socket() ^ >>> val s = Socket(10) >>> s timeout: 10, linger: 5000
Named arguments
You can use named arguments when creating new class instances. For example, given this class:
class Socket(var timeout: Int, var linger: Int) < override fun toString(): String = "timeout: $, linger: $" >
you can create a new Socket like this:
val s = Socket(timeout=2000, linger=3000)
I don’t use this feature too often, but it comes in handy in certain situations, such as when all of the class constructor parameters have the same type (such as Int in this example). For example, some people find that this code:
val s = new Socket(timeout=2000, linger=3000)
is more readable than this code:
val s = new Socket(2000, 3000)
Kotlin Constructors
As a seasoned developer, you’re likely already familiar with Spring. But Kotlin can take your developer experience with Spring to the next level!
- Add new functionality to existing classes with Kotlin extension functions.
- Use Kotlin bean definition DSL.
- Better configure your application using lateinit.
- Use sequences and default argument values to write more expressive code.
By the end of this talk, you’ll have a deeper understanding of the advanced Kotlin techniques that are available to you as a Spring developer, and be able to use them effectively in your projects.
1. Overview
In this tutorial, we’ll take a thorough look at constructors in Kotlin.
Let’s start with a quick recap of the concept: we use constructors to create objects. These look like method declarations, but always have the same name as the class and they don’t return anything.
For the setup of a Kotlin project, have a look at our introductory tutorial.
In Kotlin, a class can have a primary constructor and one or more additional secondary constructors.
In the next sections, we’ll go over each type and associated concepts.
2. Primary Constructor
The first way to create an object in Kotlin is by using a primary constructor.
This is a part of the class header. Parameters might also be class fields, which we place after the class declaration.
Let’s take a look at a basic class declaration, with two properties and a primary constructor:
class Person constructor(val name: String, val age: Int? = null)
In this example, we’ve declared properties through the val keyword. This behaves the same way as regular variables, with the mention that val properties are read-only (Java final keyword counterpart).
If we want to change the reference later, we should use the var keyword instead. However, we can’t omit the type of field in the primary constructor, as this must be explicit.
In some cases, we can omit the constructor keyword. This is only mandatory in two cases: when we use annotations, like @Autowired or access modifiers, like private or protected.
Also, we can use Kotlin default parameters in the constructors.
Next, let’s see how we can use our primary constructor:
val person = Person("John") val personWithAge = Person("Mark", 22)
We can see that a class name is a constructor invocation. There’s no need to use the new keyword.
For reference, let’s also have a look at the Java equivalent of a constructor declaration:
class PersonJava < final String name; final Integer age; public PersonJava(String name) < this.name = name; this.age = null; >public PersonJava(String name, Integer age) < this.name = name; this.age = age; >>
As you can see, Java would need much more code to achieve the same result.
2.1. Primary Constructors on the JVM
Please note that the compiler will generate an additional constructor without parameters on the JVM. For this purpose, it’ll instantiate an object with default values.
This way, Kotlin can work well with such libraries as Jackson or JPA, which use the no-args constructor to create class instances:
class Person(val name: String = "")
3. Initializing Properties from Constructor Parameters
Property initializers from the class body can use the primary constructor parameters.
Let’s transform name to the upperCaseName property:
class Person(val name: String, val age: Int? = null)
We can see the output in the console by adding the second init block:
4. Initializer Blocks
We can’t put any code in the primary constructor.
However, we sometimes have to execute some initialization code. A good place for it is an initializer block, which is prefixed with the init keyword.
The initializer block is called after the primary constructor. We can also access class fields in this place.
A class can have one or more init blocks.
Let’s append the initializer block to our Person class:
Next, when we create a Person class object, we’ll see in the console:
We would throw IllegalArgumentException for the empty surname.
When we have many init blocks, they will be executed in the same order as they appear in the class body.
5. Secondary Constructor
In a Kotlin class, we can also declare one or more secondary constructors. Secondary constructors are prefixed with the constructor keyword:
Each secondary constructor has to delegate to the primary constructor. We’ll do this by this keyword.
Let’s move our properties to the primary constructor and modify the secondary constructor:
class Car(val id: String, val type: String)
6. Constructors and Inheritance
We can use a primary constructor of the superclass.
Note that all classes in Kotlin are final by default. This means we’ll need to add the open keyword so that we can inherit from our Person class.
Let’s add an Employee class that inherits from the Person class. They are both using primary constructors:
class Employee(name: String, val salary: Int): Person(name)
By doing this, we pass a name to the primary constructor of Person class. Additionally, we add a new field called salary in the Employee class.
7. Conclusion
In this quick article, we discussed various ways of creating constructors in the Kotlin language. We can instantiate our fields in many different ways, according to our wishes.
The implementation of all our examples can be found in the Github project.
For more information about Kotlin features itself, please have a look at our introduction to Kotlin.