Object Expressions and Declarations

    To create an object of an anonymous class that inherits from some type (or types), we write:

    If a supertype has a constructor, appropriate constructor parameters must be passed to it. Many supertypes may be specified as a comma-separated list after the colon:

    1. public open val y: Int = x
    2. }
    3. interface B { /*...*/ }
    4. val ab: A = object : A(1), B {
    5. override val y = 15
    6. }

    If, by any chance, we need “just an object”, with no nontrivial supertypes, we can simply say:

    1. fun foo() {
    2. val adHoc = object {
    3. var x: Int = 0
    4. var y: Int = 0
    5. }
    6. print(adHoc.x + adHoc.y)
    7. }

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

    1. class C {
    2. // Private function, so the return type is the anonymous object type
    3. val x: String = "x"
    4. }
    5. // Public function, so the return type is Any
    6. fun publicFoo() = object {
    7. val x: String = "x"
    8. }
    9. fun bar() {
    10. val x1 = foo().x // Works
    11. val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
    12. }
    13. }

    The code in object expressions can access variables from the enclosing scope.

    Object declarations

    1. object DataProviderManager {
    2. fun registerDataProvider(provider: DataProvider) {
    3. // ...
    4. }
    5. val allDataProviders: Collection<DataProvider>
    6. get() = // ...
    7. }

    This is called an object declaration, and it always has a name following the object keyword. Just like a variable declaration, an object declaration is not an expression, and cannot be used on the right hand side of an assignment statement.

    Object declaration’s initialization is thread-safe and done at first access.

    To refer to the object, we use its name directly:

    1. DataProviderManager.registerDataProvider(...)

    Such objects can have supertypes:

    1. override fun mouseClicked(e: MouseEvent) { ... }
    2. override fun mouseEntered(e: MouseEvent) { ... }
    3. }

    NOTE: object declarations can’t be local (i.e. be nested directly inside a function), but they can be nested into other object declarations or non-inner classes.

    Members of the companion object can be called by using simply the class name as the qualifier:

    1. val instance = MyClass.create()

    The name of the companion object can be omitted, in which case the name Companion will be used:

    1. class MyClass {
    2. companion object { }
    3. }
    4. val x = MyClass.Companion

    The name of a class used by itself (not as a qualifier to another name) acts as a reference to the companion object of the class (whether named or not):

    1. class MyClass1 {
    2. companion object Named { }
    3. }
    4. val x = MyClass1
    5. class MyClass2 {
    6. companion object { }
    7. }
    8. val y = MyClass2

    Note that, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces:

    However, on the JVM you can have members of companion objects generated as real static methods and fields, if you use the @JvmStatic annotation. See the section for more details.

    Semantic difference between object expressions and declarations

    • object expressions are executed (and initialized) immediately, where they are used;
    • a companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer.