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

cover

The Object Singleton

Given that Kotlin is a class-based object, oriented-language, if you want to create an object you first create a class and then use it to create instances of the object. The class is the blueprint for the object and by calling the constructor you can create as many instances of the object as you like. 

This is usually what you want to do, but occasionally there is a need for just one instance of an object and in this case having to create a class seems like a round about way to work. Many languages allow you to create an object directly without the use of a class and Kotlin allows you to do this. 

You can declare an object using very similar syntax to declaring a class. For example:

object myObject {
    fun say(message: String) {
        println(message)
    }
}

creates an object instance called myObject with a single say method. Directly following this declaration, you can call the method and there is no need to instantiate the object - indeed you can't as there is no class and no constructor. You use the object in the same way as you would any object:

myObject.say("Hello World")

There are some restrictions to be aware of. In particular, an object declaration cannot appear inside a function, including main. They can appear as part of an object or class declaration. You cannot declare an object in an inner class, however. 

The object declaration is a perfect way to implement the singleton pattern as, without a constructor, there can only be one instance.

Class Variations

As well as being useful in creating singletons, you can also use object declarations as a way of creating a slight variation on an existing class without having to go to the trouble of deriving a subclass.  All you have to do is include the class that you want to base your object on in the declaration:

object myObject:myClass() {
    fun say(message: String) {
        println(message)
    }

In this case myObject inherits all of the methods and properties defined in myClass. You can now add methods and properties to myObject and you can override the inherited methods and properties.

An object can also implement an interface. Inheritance is described in full detail in the next chapter, but for this to work myClass has to be open and you have to use the override keyword on any method or property you redefine. 

This is a very good way of creating ad-hoc objects without having to implement a full inheritance hierarchy. In some projects this fits with what you are trying to do; in others it is simply bad design.

Static Members & Companion Objects

If you know your Java, or almost any object-oriented language, you might be wondering where static methods and properties are in Kotlin?

In Java you can define a method as static:

class myClass{   
  public static String myMethod() {
       return "Hello World";
  }
}

The static qualifier means you can now call the method as if it was a method of the class, i.e.

myClass.myMethod()

The purpose of static methods is to provide utility methods that don't depend on the particular instance of the class or to provide utility methods that behave as if they were defined on a singleton.

Kotlin doesn't support static members. That is, you cannot define a method or property that belongs to the class.

What you can do instead is create a companion object inside the class. This might seem like a strange idea at first. but when you declare a static method you are treating the class as if it was an object in its own right. A companion object has the same name as the class and therefore any methods that you give it look like static methods. To declare a companion object all you have to do is use the keyword companion in front of the object's declaration. Of course, to be a companion it has to be declared within a class. For example:

class myClass {
    companion object {
        fun myMethod() {
            println("Hello World")
        }
    }
}

defines a companion object for myClass with a single method, myMethod, which can be called using:

myClass.myMethod()

Notice that as objects cannot be local neither can a companion object and so the class has to be a declared at the top level and not within a function, say. 

You can access the companion variable as a complete object using the default name Companion or a custom name.

For example:

companion object myCompanion ...

For example, you can call myMethod using:

myClass.Companion.myMethod()

if you haven't assigned a custom object name.

Notice that while the companion object looks like a set of static members, it is a real object and can be used like a singleton object. For example, it can inherit from another class and it can implement interfaces.

This is an interesting new feature and it raises the idea of static members to another level. It provides a way to provide class-related utility functions as part of the class. However, the documentation advises that you should use package level functions in preference to objects, but notice that this approach doesn't allow you to associate functions with a particular class

Object Expressions

The ability to create single instances of an object is a very useful facility, but often it is more convenient to use the object at once without the bother of giving it a name or a long and permanent life. For example, if a function accepts an object as a parameter, then going to the trouble of creating a class with modifications and an instance is far too much work. Even declaring and customizing a top level object complete with a name is more work than the problem warrants and the object exists for the lifetime of the program. 

Put simply, sometimes all we want is an anonymous object that is a slight modification of an existing class and which will have a relatively short life.

In Java the solution is to use an anonymous inner class.

In Kotlin the solution is to use an object expression. 

The way to think about an object expression is that it is an expression which is evaluated at the point in the program at which it occurs and which evaluates to an object.

Unlike an object declaration, which cannot be local, an object expression has to be local. 

Finding an example of using an object expression that is simple isn't easy because the most common usage is to pass an object to a Java function - usually an event handler.

Although Kotlin has its own timer object, we can make use of the Swing timer, the constructor of which takes an integer delay in milliseconds, and an instance of ActionListener.

ActionListener is an interface which has a single actionPerformed method, which has to be defined to handle the timer interrupt. If  you don't know about interfaces see the next chapter. The important point is that we are creating an object directly from a class or interface definition. 

We can use the Swing timer by adding an import:

import javax.swing.Timer

To create a instance of Timer we have to supply the ActionListener object and, as it will only be used by the timer, what better than to use an object expression:

var timer = Timer(1000, object : ActionListener {
        override fun actionPerformed(e: ActionEvent?) {
            println("Action Performed")
        }
    })

If you are using IntelliJ you can use it to generate the override. Notice that this has to be inside a function, for example main, for it to work.

If you now start the timer and put the program into an infinite loop to keep it running, you will see the message printed every second:

 timer.start()
    while(true){}

Notice that the only difference between an object declaration and an object expression is that it is used as an expression, i.e. it is assigned to a variable or passed as a parameter. 

If you want to, you can store the result of an object expression in a variable:

val action=object : ActionListener {
        override fun actionPerformed(e: ActionEvent?) {
            println("Action Performed")
        }
    }

and then this can be passed to the timer constructor:

var timer = Timer(1000,action)

An object expression can inherit from one class and any number of interfaces. You can use any variables that are in scope when the expression is evaluated. i.e. local and global variables accessible in the function at the point the expression is being evaluated. Java inner classes can only access variables that have been declared final.

The only restriction is that, if you return an object expression as the result of a function, it has the type of its super class and any members you have added are not accessible. 

The documentation makes a point of emphasizing the differences between an object declaration and an object expression, but if you understand the difference between a general expression and declaration these should be obvious. 

Object expressions are evaluated when they are used, i.e. when they are executed in the flow of control. By contrast, object declarations are evaluated lazily, i.e only when the object they declare is needed.  A companion object can only be a declaration and it is initialized when its class is loaded.  

Summary

  • Every class has a primary constructor, even if it is just the default parameterless constructor provided by the system

  • The primary constructor is just a declaration of parameters that can be used when an instance is created.

  • You can use the parameters within the properties and methods defined in the class definition.

  • Usually the parameters in the primary constructor are used to automatically create properties using var or val.

  • If you want to do more in the constructor, or have overloaded constructors, you need to define secondary constructors.

  • Secondary constructors ??have a method body and can execute code. 

  • A secondary constructor always has to call the primary constructor using the this(parameters) statement.

  • A class can also have any number of initializer blocks which are executed after the constructor is called, but before any constructor body is executed. 

 

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

 

Banner


VLOGGER - AI Does Talking Heads
24/03/2024

Developed by Google researchers VLOGGER AI is a system that can create realistic videos of people talking and moving from a single still image and an audio clip as input. 



Grafana 11 Improves Metrics
11/04/2024

Grafana Labs, creators of the Grafana open-source metrics analytics and visualization suite, has announced the preview release of Grafana 11 with improvements to make it easier to view metrics, and ch [ ... ]


More News

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 24 July 2017 )