The Programmers Guide To Kotlin - The Class & The Object
The Programmers Guide To Kotlin - The Class & The Object
Written by Mike James   
Monday, 24 July 2017
Article Index
The Programmers Guide To Kotlin - The Class & The Object
Class Members - Methods & Properties
Static Members & Companion Objects

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 in the order the code is listed and before the constructor is run, but with the constructor parameters fully defined. That is the intializer looks like a block of code that is inserted into the very start of every constructor.

For example:

class MyClass(Name:String) {
        init {
            println(Name)
        }
 
}
   
var 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, 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 with 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 constructor 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 initalizer 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. 

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 and 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 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 and 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 and 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 printlin calls the getter.

If you want to call Java code then you can use getter and 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.

Late Initialized Properties

Much of this chapter has been about setting up and initializing properties. If the property is a non-null type then it has to be initialized in the constructor code. However there are case where the property is more correctly initialized by some other function. You can deal with this by initializing the property to a default value or you can use a nullable type and have to deal with null checks from then on or you can mark the property with lateinit.  This only works for var non-null, non-primitive properties defined in the body of the class. The property can remain undefined for as long as you like, but if you try to access it before it is initalized you will generate an exception. For example:

class myClass {
        lateinit var myMessage: String
        fun setMessage() {
            myMessage = "Message of the day"
        }
    }

In this case it is supposed that the setMessage function will be called before the property is used. If you do use it uninitialized:

var myObject = myClass()
println(myObject.myMessage)

then you will see the error message:

Exception in thread "main"
kotlin. UninitializedPropertyAccessException:
lateinit property myMessage has not been
initialized at MainKt$main$myClass.
getMyMessage(main.kt:10) 

However if you do initalize it:

var myObject = myClass()
myObject.setMessage()
println(myObject.myMessage)

everything works.

Class Members - Inner Classes

Classes can have functions and properties as members and they can also have other classes. A nested class is defined within another class. It is local to the containing or outer class in that it isn't visible outside of the containing class. For  example, MyNestedClass is local to MyClassA:

class MyClassA {
    class MyNestedClass {
        fun myInnerMethod() {
            println("myMethod")
        }
    }    fun myOuterMethod() {
        var myNestedObject = MyNestedClass()
        myNestedObject.myInnerMethod()

    }
}

It can be used by methods inside MyClassA but it is inaccessible from anywhere else. 

By default nested classes cannot access the members of the outer class. That is, if you try:

class MyClassA {
    var myString:String="Hello World"
     class MyNestedClass {
        fun myInnerMethod() {
            println(myString)
        }
   

    }

you will find that myString is unresolved and there is an error.

However, if you mark the nested class as inner then it can access the outer class's members That is, with the addition of inner, myInnerMethod can now access myString in the outer class:

class MyClassA {
   var myString:String="Hello World"
   inner class MyInnerClass {
        fun myInnerMethod() {
            println(myString)
        }
   

   }

Inner classes are useful if you need to define classes that are useful to another class and not generally useful. If you need to instantiate a lot of inner class  objects then it makes sense. However, if you only need a single instance then using a Kotlin object makes better sense as you don't have to declare a class first. 



Last Updated ( Monday, 24 July 2017 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2017 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.