Kotlin Object Expressions and Object Declarations

Kotlin uses object expressions and object declarations to create an object of a class with slight modifications, without needing to declare a new subclass.


Object Expressions

Use an object expression to create an anonymous inner class object for use as a method parameter:

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }
    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})

The object can inherit from a base class, or implement other interfaces:

open class A(x: Int) {
    public open val y: Int = x
}

interface B {……}

val ab: A = object : A(1), B {
    override val y = 15
}

If the supertype has a constructor, parameters must be passed to it. Multiple supertypes and interfaces can be separated by commas.

Through an object expression, you can directly obtain an object without going through a class definition:

fun main(args: Array<String>) {
    val site = object {
        var name: String = "Example"
        var url: String = "www.example.com"
    }
    println(site.name)
    println(site.url)
}

Note that anonymous objects can be used as types declared only in local and private scopes. If you use an anonymous object as the return type of a public function or as the type of a public property, the actual type of that function or property will be the supertype declared by the anonymous object, or Any if no supertype is declared. Members added in the anonymous object will not be accessible.

class C {
    // 私有函数,所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有函数,所以其返回类型是 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // 没问题
        val x2 = publicFoo().x  // 错误:未能解析的引用“x”
    }
}

In an object expression, other variables in the scope can be conveniently accessed:

fun countClicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0

    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }

        override fun mouseEntered(e: MouseEvent) {
            enterCount++
        }
    })
    // ……
}

Object Declarations

Kotlin uses the `object` keyword to declare an object.

In Kotlin, we can conveniently obtain a singleton through object declarations.

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}

To reference this object, we can simply use its name:

DataProviderManager.registerDataProvider(……)

Of course, you can also define a variable to obtain this object. However, when you define two different variables to obtain this object, you will find that you cannot get two different variables. In other words, through this approach, we obtain a singleton.

var data1 = DataProviderManager
var data2 = DataProviderManager
data1.name = "test"
print("data1 name = ${data2.name}")  

Example

In the following example, both objects output the same URL address:

object Site {
    var url:String = ""
    val name: String = "Example"
}
fun main(args: Array<String>) {
    var s1 =  Site
    var s2 = Site
    s1.url = "www.example.com"
    println(s1.url)
    println(s2.url)
}

The output result is:

www.example.com
www.example.com

Objects can have supertypes:

object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ……
    }

    override fun mouseEntered(e: MouseEvent) {
        // ……
    }
}

Unlike object expressions, when an object is declared inside another class, this object cannot be accessed through an instance of the outer class, but only through the class name. Likewise, the object cannot directly access the methods and variables of the outer class.

class Site {
    var name = "Example"
    object DeskTop{
        var url = "www.example.com"
        fun showName(){
            print{"desk legs $name"} // 错误,不能访问到外部类的方法和变量
        }
    }
}
fun main(args: Array<String>) {
    var site = Site()
    site.DeskTop.url // 错误,不能通过外部类的实例访问到该对象
    Site.DeskTop.url // 正确
}

Companion Objects

An object declaration inside a class can be marked with the `companion` keyword, so that it is associated with the outer class, and we can directly access the object's internal elements through the outer class.

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

val instance = MyClass.create()   // 访问到对象的内部元素

We can omit the object name of the object, and then use `Companion` to replace the name that needs to be declared:

class MyClass {
    companion object {
    }
}

val x = MyClass.Companion

Note:A class can only declare one internally associated object, i.e., the `companion` keyword can only be used once.

The members of a companion object look like static members in other languages, but at runtime they are still instance members of a real object. For example, it can also implement interfaces:

interface Factory<T> {
    fun create(): T
}


class MyClass {
    companion object : Factory<MyClass> {
        override fun create(): MyClass = MyClass()
    }
}

Semantic Differences Between Object Expressions and Object Declarations

There is an important semantic difference between object expressions and object declarations:

  • Object expressions are executed immediately where they are used.

  • Object declarations are lazily initialized when first accessed.

  • The initialization of a companion object occurs when the corresponding class is loaded (resolved), matching the semantics of Java static initializers.

Other Extensions