Object keyword in Kotlin

Kotlin has an object keyword, which combines both declaring a class and creating an instance of it in one action. It can be used in three different situations and has three different meanings. Let's take a look at all of them:

  • Object declaration. Defines a singleton class.
  • Companion object. Defines a nested class that can hold members related to the outer containing class. These members can't require an instance of the outer class.
  • Object expression. Creates an instance of the object on the fly, the same as Java's anonymous inner classes.

Singletons with object keyword

Sometimes, your program has to have only one instance of a certain type. This pattern is known as a singleton. In other languages, you would have to implement this pattern manually, making sure that only one instance of your type gets created. But, Kotlin has language support for creating singletons with the object keyword. Here is an example:

object Singleton {
    fun sayMyName() {
        println("I'm a singleton")
    }
}

This will both declare a Singleton class and create an instance of it. Notice how we haven't defined a constructor. Since the compiler creates an instance for you, if you tried to declare a constructor or create an instance yourself, you'd get a compiler error.

If you need to access a member from an object, you can do so by first accessing the object type and then the member you wish to invoke. Here's how to call a function from an object:

Singleton.sayMyName()

Other than not having a constructor, there aren't any differences from normal classes. Objects can have properties and functions; they can also implement interfaces and extend other classes. Here is an object that implements the Runnable interface:

object RunnableSingleton : Runnable {
    override fun run() {
        println("I'm a runnable singleton")
    }
}

Kotlin also allows objects to be declared inside classes; then, they become nested singletons.

Nested objects cannot have an inner keyword; they cannot access members from the outer class. Here is how you would declare an object inside a class:

class Outer {
    object Singleton {

    }
}

Finally, if you need to some kind of initialization of your object, you can do it inside the init block. The init block will be called when your object gets constructed:

object SingletonWithInitializer {
    var name = ""

    init {
        name = "Singleton"
    }
}

Companion objects

Java has a static keyword that can be applied to class members. Static members belong to a type, and not to an instance of a type. Kotlin doesn't have a static keyword, but can achieve this kind of functionality with some other language features. A common use case for static functions is grouping some utility functions inside a class. The collections class from the java.util package is a perfect example of this.

You can have this in Kotlin with functions declared at the file level or inside an object.

Another use of static members could be factory methods. This is usually done by hiding the class constructor from the outside with a private access modifier and delegating the instance construction to static methods.

Factory methods can be useful and sometimes they are preferred to having multiple constructors. In Kotlin, companion objects can be used to implement this design pattern.

Here's how a factory method design pattern would be implemented in Kotlin:

class User private constructor(val userId: String) {
    companion object {
        fun newUserWithEmail(email: String): User {
            return User(email)
        }

        fun newUserFromUUID(uuid: UUID): User {
            return User(uuid.toString())
        }
    }
}

Accessing the companion object and its members is the same as if they were static:

val userFromEmail = User.newUserWithEmail("john@email.com")
val userFromUUID = User.newUserFromUUID(UUID.randomUUID())

The same as with normal objects, companion objects cannot have constructors, but can extend other classes and implement interfaces.

Anonymous objects

The object keyword has one more use, creating anonymous objects. If you are familiar with Java, then you probably had experience with anonymous inner classes. Anonymous objects are similar to them. With them, you create objects on the fly. Let's create an anonymous object of a Runnable interface:

Thread(object : Runnable {
    override fun run() {
        println("I'm created with anonymous object")
    }
}).run()

The syntax for creating anonymous objects is similar to singleton objects, but without the name of the object. The name in most cases is not needed. If you need a name for your anonymous object, or need it to store for later use, you can initialize a variable with it. This example shows it:

val runnable = object : Runnable {
    override fun run() {
        println("I'm created with anonymous object")
    }
}

Anonymous objects are not restricted to interfaces, you can also create classes with them. Here's how you would create an anonymous object of an abstract class. Notice how we had to call the constructor of the class:

val writer = object : Writer() {
    override fun write(cbuf: CharArray, off: Int, len: Int) {
        // implementation omitted
    }

    override fun flush() {
        // implementation omitted
    }

    override fun close() {
        // implementation omitted
    }
}