조용한 담장

코틀린(Kotlin) 클래스(Class) : 객체 표현식 과 선언 (Object Expressions and Declarations) 본문

kotlin

코틀린(Kotlin) 클래스(Class) : 객체 표현식 과 선언 (Object Expressions and Declarations)

iosroid 2020. 1. 2. 16:05

코틀린 클래스의 Object Expressions and Declarations 를 살펴보자

원문 https://kotlinlang.org/docs/reference/object-declarations.html 을 보며 정리.

새로운 클래스를 만들지 않고 특정 클래스에 약간의 수정만 한 오브젝트를 만드는 방법에 대해 살펴본다.

Object expressions

특정 type 을 상속한 익명(이름없는) 클래스(anonymous class) 의 오브젝트를 생성하는 방법

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

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

supertype 이 생성자를 가지고 있으면 파라미터를 전달해 줘야 한다.

: 뒤에 , 로 구분된 리스트 식으로 추가한다.

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

interface B { /*...*/ }

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

만약 정말 단순한 오브젝트만 필요하다면 아래처럼 쓴다.

fun foo() {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}

익명(이름없는) 오브젝트 (anonymous object) 는 local 과 private 선언에서만 type 으로써 사용될 수 있다.

익명 오브젝트 (anonymous object) 를 public 함수의 리턴 타입으로 쓰거나 public property 의 타입으로 쓰면 둘의 실제 타입은 anonymous object 의 선언된 supertype 되거나, supertype 을 선언 안한 경우엔 Any 이 되게 된다.

anonymous object 에 추가된 멤버들은 접근이 불가능 해 진다.

class C {
    // Private function, so the return type is the anonymous object type
    private fun foo() = object {
        val x: String = "x"
    }

    // Public function, so the return type is Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // Works
        val x2 = publicFoo().x  // ERROR: Unresolved reference 'x'
    }
}

object expressions 안에 있는 코드는 외부 영역 (enclosing scope) 의 변수에 접근이 가능하다.

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 에서 싱글톤(singleton) 은 아래와 같이 선언할 수 있다.

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

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

이는 object declaration 이라 하며 object 키워드를 사용한다.

object declaration 은 expression 이 아니므로 할당 구문 (assignment statement) 의 오른쪽에 쓰일 수 없다.
아래와 같이 사용한다.

DataProviderManager.registerDataProvider(...)

supertype 을 가질 수 있다.

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

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

object declaration 은 함수 안에 직접 중첩되어 선언되는 것 같이 로컬에 위치할 수 없지만, 다른 object declaration 이나 inner class 가 아닌 것에 중첩될 수는 있다.

Companion Objects

클래스 안에 위치하는 object declaration 은 companion 키워드가 적용될 수 있다.

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

companion object 의 멤버들은 클래스의 멤버처럼 호출할 수 있다.

val instance = MyClass.create()

companion object 의 이름 대신에 Companion 으로 사용될 수 있다.

class MyClass {
    companion object { }
}

val x = MyClass.Companion

클래스의 이름이 companion object 의 레퍼런스 처럼 동작한다.

class MyClass1 {
    companion object Named { }
}

val x = MyClass1

class MyClass2 {
    companion object { }
}

val y = MyClass2

companion object 의 멤버들이 다른 언어의 static 멤버처럼 보이지만, 런타임시에 실제 오브젝트의 멤버들의 인스턴스 라는 점이 다르며, 인터페이스를 구현할 수 있다는 것이 다르다.

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

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

val f: Factory<MyClass> = MyClass

JVM 에서는 @JvmStatic 표시를 쓰면 실제 static method 와 field 로 생성된 companion object 의 멤버들을 쓸 수 있다.

참조 : Java interoperability

Semantic difference between object expressions and declarations

object expressions 과 object declarations 의 중요한 semantic difference 가 있다.

  • object expressions 는 사용되는 곳에서 바로 초기화 되고 실행된다.
  • object declarations 는 처음 접근이 있을 때 늦은 초기화가 이루어 진다.
  • companion object 는 해당 클래스가 로드되고, Java static initializer 의 semantics 와 매칭될 때 초기화 된다.

 

Comments