The Programmers Guide To Kotlin - The Class & The Object
Written by Mike James   
Tuesday, 29 October 2024
Article Index
The Programmers Guide To Kotlin - The Class & The Object
Secondary Constructors
Late Initialized Properties
Static Members & Companion Objects
Object Expressions
Value Classes

Secondary Constructors

For a great many classes a primary constructor is all that is needed because initializing an instance comes down to creating and initializing some properties. If your class needs more initialization than this, or if you need to allow alternative forms of initialization, then you need to define secondary constructors.

These are methods prefixed by the keyword constructor – you don't have to give them names as Kotlin understands constructors. You can put the keyword constructor in front of the primary constructor if you want to, and you have to if you put any access specifier or annotation on the line. Also notice that you don't have to have an explicit primary constructor - if you don't want one just omit the () and parameters after the class name. Kotlin automatically creates a parameterless primary constructor for you.

For example:

    class MyClass{
            constructor(Name:String){
            println(Name)
        }
    }

is a class without an explicit primary constructor, and with a single secondary constructor that accepts a single string. You can have multiple overloaded constructors and the one that matches the signature of the call will be used.

Once you have defined a primary constructor then the secondary constructors have to call it using:

:this(parameters)

before the body of the constructor and you do have to supply any parameters that don't have defaults, for example:

class MyClass(var Name:String = ""){
      constructor(Name:String,Address:String):this(Name){
println(Name)
}
}

If a secondary constructor delegates to another constructor by calling it, then it doesn't have to call the primary, but the delegated constructor does. What all this comes down to is that the primary constructor has to be called somehow.

Also notice that the trick of including var and val to automatically create properties only works with the primary constructor.



 kotlinlogo

Initializer Blocks

There is one more twist in the Kotlin class declaration - you can define initializer blocks. These are blocks of code within the class declaration that are run before the constructor. Even though they run before the constructor, the parameters passed to the constructor are available to them – they act like code inserted into the start of the constructor.

In Kotlin all initialization occurs before the constructor is run and in the order the code is listed, but with the constructor parameters fully defined. This means that the initializer really does look like a block of code that is inserted into the very start of every constructor.

For example:

class MyClass(Name:String) {
        init {
            println(Name)
        } 
}    
val myObject = MyClass("Mickey Mouse")

The init block runs before the constructor, but Name is set to "Mickey Mouse". This order of execution can be confusing at first, but it is simple and logical and, as long as you understand it, shouldn't cause any errors. You can have as many init blocks and as many constructors as you need and you don't need to define an explicit primary constructor.

Constructor Chaos?

At this point you might be confused as to how all these facilities fit together – it seems we have too many ways of defining what can happen when an instance is created. The important thing to note about the initializer is that it runs as if it was the initial code for each of the constructors – hence it is the ideal way to carry out anything that has to happen irrespective of which constructor is used.

When creating a class you have a number of different levels of constructor complexity you can adopt:

  1. The simplest is just to use a primary constructor to create a class with properties corresponding to the parameters used.

  2. Next you can define an initialization block which defines what happens when any constructor, including the primary constructor, is called.

  3. You can define any number of overloaded constructors that allow the user to create instances in different ways.

  4. Finally you can add code to each of the overloaded constructors that defines the unique actions that need to be performed when that particular constructor is used.

In most cases all you need is a primary constructor and possibly an initializer block.

Class Members – Methods & Properties

Of course, as well as constructors and init blocks, a class can have properties and methods, and so far we haven't said much about them,

There isn't much to say about methods – you simply define a function within a class and call it using the usual dot notation. Every method receives a this variable, which behaves like an extra system provided parameter set to reference the current instance. In Kotlin this works much as it does in Java and other languages, see Chapter 10 for an in-depth discussion.

There are a few things to say about properties, however. Properties can be read/write or read-only as declared using var or val.

Properties declared using var have default getters and setters and those declared using val have only a getter i.e. val properties are read-only.

All properties are accessed via getter/setter functions and, unlike in Java, you don't have to explicitly create them – the compiler will do the job for you. It will also automatically put get or set in front of the property's name so that you can use them as Java properties implemented in this way without having to modify property names.

Notice that a class cannot have a simple field, i.e. a variable that is directly accessible without the use of a getter or setter. All variables defined within a class declaration are properties.

If you want a custom getter or setter you simply define them after the property declaration. The only thing you have to remember is that the property has a default backing field which can be accessed using the identifier field. This means that within the getter/setter you refer to the property value as field.

For example:

class MyClass(Name: String) { 
    var myProp: Int = 1
       set(value) {
             if (value > 0) {
                  field = value
             }
       }
       get() = field
     }

In this case the setter will only change the value of the property if it is positive.

Also notice that, unlike in languages such as Java, you don't have to call the getters and setters. You simply assign and make use of the property and the getter/setter is called as needed.

For example:

var myObject = MyClass("Mickey Mouse")
myObject.myProp = 3
println(myObject.myProp)

To be clear, the assignment to myProp calls the setter, and the use of myProp in the println calls the getter.

If you want to call Java code then you can use getter/setter Java properties as if they were Kotlin properties, i.e. without putting get or set into the property name or calling a function. If you want to call a Kotlin method from Java you simply use the corresponding getProperty and setProperty functions.

Behind the scenes, Kotlin uses the same code to create getter/setter properties as Java.

If you want to have your own backing variable you can simply declare one and use it:

class MyClass(Name: String) {
 private var myBacking:Int=0
 var myProp: Int
     set(value) {
                 if (value > 0) {
                    myBacking = value
                 }
            }
     get() = myBacking  
 init {
         println(Name)
       }
}

Notice that in this case you can't initialize the property because it doesn't have a default backing variable. Instead you have to initialize the backing variable itself.



Last Updated ( Tuesday, 29 October 2024 )