Hanbit the Developer

Kotlin Documentation | Object expressions and declarations 본문

Mobile/Kotlin

Kotlin Documentation | Object expressions and declarations

hanbikan 2023. 5. 23. 16:09

Kotlin Documentation 시리즈에 대해

Category: Concepts - Classes and objects

문서 링크: https://kotlinlang.org/docs/object-declarations.html


Object expressions

객체를 일시적으로 사용할 때 유용하다:

val helloWorld = object {
    val hello = "Hello"
    val world = "World"
    // object expressions extend Any, so `override` is required on `toString()`
    override fun toString() = "$hello $world"
}

Inheriting anonymous objects from subtypes

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

    override fun mouseEntered(e: MouseEvent) { /*...*/ }
})

Using anonymous objects as return and value types

interface A {
    fun funFromA() {}
}
interface B

class C {
    // The return type is Any; x is not accessible
    fun getObject() = object {
        val x: String = "x"
    }

    // The return type is A; x is not accessible
    fun getObjectA() = object: A {
        override fun funFromA() {}
        val x: String = "x"
    }

    // The return type is B; funFromA() and x are not accessible
    fun getObjectB(): B = object: A, B { // explicit return type is required
        override fun funFromA() {}
        val x: String = "x"
    }
}

val x에 접근할 수 없는 이유는 object expression을 통해 임시로 정의되어서 외부에서 인터페이스를 확인할 수 없기 때문이다.(getObjectA()로 얻은 인스턴스에 대해서는 funFromA() 호출이 가능하다.)

Accessing variables from anonymous objects

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**++
        }
    })
    // ...
}

anonymous object에서 해당 scope 내의 변수에 접근할 수 있다.

*이것이 가능한 이유는 object expression이 로컬 변수처럼 작동하기 때문이다. 다음은 코틀린 코드와 디컴파일된 자바 코드이다:

fun countClicks() {
    var clickCount = 0

    val obj = object {
        fun addClickCount() {
            clickCount++
        }
    }
}
public final class DataSourceModuleKt {
   public static final void countClicks() {
      final IntRef clickCount = new IntRef();
      clickCount.element = 0;
      Object var10000 = new Object() {
         public final void addClickCount() {
            int var10001 = clickCount.element++;
         }
      };
   }
}

Object

싱글톤 객체를 정의하기 쉽다:

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

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

Data objects

data class와 비슷하며 equals(), toString(), hashCode()을 자동으로 생성한다.

data object MyDataObject {
    val x: Int = 3
}

fun main() {
    println(MyDataObject) // MyDataObject
}

Companion objects

companion 키워드로 클래스 내부에서의 object를 정의한다:

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

val instance = MyClass.create()
val x = MyClass.Companion

Without name:

class MyClass2 {
    companion object { }
}

val y = MyClass2.Companion

@JvmStatic 주석을 사용하게 되면 companion object를 실제 스태틱 메소드나 필드로 생성할 수 있다. Java에서 MyFragment.Companion.newInstance()로 호출해야만 했던 것을 MyFragment.newInstance()로 호출할 수 있게 되는 것이다:

class MyClass {
    companion object {
        val a = 1
    }
}

class MyClassWithJvmStatic {
    companion object {
        @JvmStatic
        val b = 2
    }
}
public final class MyClass {
   private static final int a = 1;
   @NotNull
   public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);

   @Metadata(
      mv = {1, 7, 1},
      k = 1,
      d1 = {"\\u0000\\u0014\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\b\\n\\u0002\\b\\u0003\\b\\u0086\\u0003\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002¢\\u0006\\u0002\\u0010\\u0002R\\u0014\\u0010\\u0003\\u001a\\u00020\\u0004X\\u0086D¢\\u0006\\b\\n\\u0000\\u001a\\u0004\\b\\u0005\\u0010\\u0006¨\\u0006\\u0007"},
      d2 = {"Lcom/hanbikan/core/database/entity/MyClass$Companion;", "", "()V", "a", "", "getA", "()I", "core-database_debug"}
   )
   public static final class Companion {
      public final int getA() {
         return MyClass.a;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

public final class MyClassWithJvmStatic {
   private static final int b = 2;
   @NotNull
   public static final MyClassWithJvmStatic.Companion Companion = new MyClassWithJvmStatic.Companion((DefaultConstructorMarker)null);

   public static final int getB() {
      MyClassWithJvmStatic.Companion var10000 = Companion;
      return b;
   }

   @Metadata(
      mv = {1, 7, 1},
      k = 1,
      d1 = {"\\u0000\\u0014\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\b\\n\\u0002\\b\\u0004\\b\\u0086\\u0003\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002¢\\u0006\\u0002\\u0010\\u0002R\\u001c\\u0010\\u0003\\u001a\\u00020\\u00048\\u0006X\\u0087D¢\\u0006\\u000e\\n\\u0000\\u0012\\u0004\\b\\u0005\\u0010\\u0002\\u001a\\u0004\\b\\u0006\\u0010\\u0007¨\\u0006\\b"},
      d2 = {"Lcom/hanbikan/core/database/entity/MyClassWithJvmStatic$Companion;", "", "()V", "b", "", "getB$annotations", "getB", "()I", "core-database_debug"}
   )
   public static final class Companion {
      /** @deprecated */
      // $FF: synthetic method
      @JvmStatic
      public static void getB$annotations() {
      }

      public final int getB() {
         return MyClassWithJvmStatic.b;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

Semantic difference between object expressions and declarations

object expression과 object declaration의 의미상 차이는 다음과 같다:

  • object expression은 코드에서 즉시 수행된다.(Object expressions are executed (and initialized) immediately, where they are used.)
  • object declaration은 해당 오브젝트가 액세스될 때 lazily하게 초기화된다.(Object declarations are initialized lazily, when accessed for the first time.)
  • companion object는 상응하는 클래스가 로드될 때 초기화된다.(A companion object is initialized when the corresponding class is loaded (resolved) that matches the semantics of a Java static initializer.)

*”클래스가 클래스 로더에 의해 로드되는 시점은 JVM(Java Virtual Machine)에서 클래스가 처음으로 참조될 때입니다." - GPT 4.0