Constructors

When creating a new object, you’ll typically initialize it by passing information to a constructor.

A constructor is a special function that creates a new object. Just like a function, you pass information to a constructor using an argument list. The constructor argument list looks like a function argument list, but is placed directly after the class name:

// Constructors/Arg.kt class Alien(name: String) { val greeting = "Poor $name!" } fun main(args: Array<String>) { val alien = Alien("Mr.Meeseeks") println(alien.greeting) // alien.name // Error // [1] } /* Output: Poor Mr.Meeseeks! */

Creating an Alien object requires an argument (try it without one). The name argument is used to initialize the greeting property. However, name is not accessible outside the class body—try uncommenting the last line in main() to see the error.

If you want the constructor argument to be visible outside the class body, declare it as a var or val in the argument list:

// Constructors/VisibleArgs.kt class MutableNameAlien(var name: String) class FixedNameAlien(val name: String) fun main(args: Array<String>) { val alien1 = MutableNameAlien("Reverse Giraffe") val alien2 = FixedNameAlien("Krombopolis Michael") alien1.name = "Parasite" // Can't do this: // alien2.name = "Parasite" }

These class definitions have no explicit class bodies—the bodies are implied. Because name is declared using var or val, it becomes a property and is accessible outside the class body. Constructor arguments that are declared with val cannot be changed, but those that are declared with var can, as you expect.

Your class can have many constructor arguments:

// Constructors/MultipleArgs.kt class AlienSpecies( val name: String, val eyes: Int, val hands: Int, val legs: Int ) { fun represent() = "$name with $eyes eyes, " + "$hands hands and $legs legs" } fun main(args: Array<String>) { val shim = AlienSpecies("Shimshamian", 3, 2, 2) val grung = AlienSpecies("Grunglokian", 2, 4, 2) println(shim.represent()) println(grung.represent()) } /* Output: Shimshamian with 3 eyes, 2 hands and 2 legs Grunglokian with 2 eyes, 4 hands and 2 legs */

All our classes so far are initialized in a trivial way: constructor arguments are assigned to properties. Constructors may contain non-trivial initialization logic as well, which is explained in [Non-trivial constructors] later.

Whenever you use an object in a situation that expects a String, Kotlin produces a String representation of that object by calling its toString() member function. If you don’t write one, you still get a default toString():

// Constructors/DisplayAlienSpecies.kt fun main(args: Array<String>) { val alien = AlienSpecies("Narduarvian", 2, 2, 4) println(alien) } /* Sample output: AlienSpecies@4d7e1886 */

The default toString() isn’t too useful: it produces the class name and the physical address of the object (which varies from one run to the next). For better results, define your own toString():

// Constructors/GoodAlien.kt class GoodAlien(val name: String) { override fun toString(): String { return "GoodAlien('$name')" } } fun main(args: Array<String>) { val birdPerson = GoodAlien("Birdperson") println(birdPerson) } /* Output: GoodAlien('Birdperson') */

Here you see a new keyword: override. This is necessary (Kotlin insists on it) because toString() is already defined (the definition that produces 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 makes it clear to the reader of the code what is happening and helps prevent mistakes.

A good toString() is useful when debugging a program. Sometimes just looking inside an object is enough to see what’s going wrong.

Previous          Next

©2018 Mindview LLC. All Rights Reserved.