Constructors
You initialize a new object by passing information to a constructor.
Each object is an isolated world. A program is a collection of objects, so correct initialization of each individual object solves a large part of the initialization problem. Kotlin includes mechanisms to guarantee proper object initialization.
A constructor is like a special member function that initializes a new object. The simplest form of a constructor is a single-line class definition:
// Constructors/Wombat.kt
class Wombat
fun main() {
val wombat = Wombat()
}
In main(), calling Wombat() creates a Wombat object. If you are coming
from another object-oriented language you might expect to see a new keyword
used here, but new would be redundant in Kotlin so it was omitted.
You pass information to a constructor using a parameter list, just like a
function. Here, the Alien constructor takes a single argument:
// Constructors/Arg.kt
class Alien(name: String) {
val greeting = "Poor $name!"
}
fun main() {
val alien = Alien("Mr. Meeseeks")
println(alien.greeting)
// alien.name // Error // [1]
}
/* Output:
Poor Mr. Meeseeks!
*/
Creating an Alien object requires the argument (try it without one). name
initializes the greeting property within the constructor, but it is not
accessible outside the constructor—try uncommenting line [1].
If you want the constructor parameter to be accessible outside the class body,
define it as a var or val in the parameter list:
// Constructors/VisibleArgs.kt
class MutableNameAlien(var name: String)
class FixedNameAlien(val name: String)
fun main() {
val alien1 =
MutableNameAlien("Reverse Giraffe")
val alien2 =
FixedNameAlien("Krombopulos Michael")
alien1.name = "Parasite"
// Can't do this:
// alien2.name = "Parasite"
}
These class definitions have no explicit class bodies—the bodies are implied.
When name is defined as a var or val, it becomes a property and is
thus accessible outside the constructor. val constructor parameters cannot be
changed, while var constructor parameters are mutable.
Your class can have numerous constructor parameters:
// Constructors/MultipleArgs.kt
class AlienSpecies(
val name: String,
val eyes: Int,
val hands: Int,
val legs: Int
) {
fun describe() =
"$name with $eyes eyes, " +
"$hands hands and $legs legs"
}
fun main() {
val kevin =
AlienSpecies("Zigerion", 2, 2, 2)
val mortyJr =
AlienSpecies("Gazorpian", 2, 6, 2)
println(kevin.describe())
println(mortyJr.describe())
}
/* Output:
Zigerion with 2 eyes, 2 hands and 2 legs
Gazorpian with 2 eyes, 6 hands and 2 legs
*/
In Complex Constructors, you’ll see that constructors can also contain complex initialization logic.
If an object is used when a String is expected, Kotlin calls the object’s
toString() member function. If you don’t write one, you still get a default
toString():
// Constructors/DisplayAlienSpecies.kt
fun main() {
val krombopulosMichael =
AlienSpecies("Gromflomite", 2, 2, 2)
println(krombopulosMichael)
}
/* Sample output:
AlienSpecies@4d7e1886
*/
The default toString() isn’t very useful—it produces the class name and the
physical address of the object (this varies from one program execution to the
next). You can define your own toString():
// Constructors/Scientist.kt
class Scientist(val name: String) {
override fun toString() =
"Scientist('$name')"
}
fun main() {
val zeep = Scientist("Zeep Xanflorp")
println(zeep)
}
/* Output:
Scientist('Zeep Xanflorp')
*/
override is a new keyword for us. It is required here because toString()
already has a definition, the one producing the primitive result. override
tells Kotlin that yes, we do actually want to replace the default toString()
with our own definition. The explicitness of override clarifies the code and
prevents mistakes.
A toString() that displays the contents of an object in a convenient form is
useful for finding and fixing programming errors. To simplify the process of
debugging, IDEs provide
debuggers that
allow you to observe each step in the execution of a program and to see inside
your objects.
Exercises and solutions can be found at www.AtomicKotlin.com.