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

 

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 roundabout 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 figure 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 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 ...

and 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, it can be passed as a parameter, and it can implement interfaces.

This is an interesting new language 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. 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 which companion objects do.



Last Updated ( Tuesday, 29 October 2024 )