Object 식과 선언

새로운 클래스를 명시적으로 선언하지않고, 클래스르 조금 변경한 객체를 만들 필요가 있다. Java에서는 익명 Inner 클래스를 사용해서 이 케이스를 해결. Kotlin은 Object 식과 Object 선언으로 약간 일반화하고 있다.

Object 식

특정 타입을 상속받는 inner 클래스 객체를 만들때 다음과 같이 기술

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

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

상위 타입이 복수이면 콜론 뒤에 콤마로 구분해서 지정한다

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)
}

익명 객체는 로컬 및 private 선언에서만 타입으로 사용 가능. 익명 객체를 public 함수의 반환 type 또는 public 속성의 타입으로 사용하는 경우 해당 함수 또는 속성의 실제 타입은 익명 객체의 선언 된 상위 타입이거나 상위 타입을 선언하지 않은 경우 Any가 된다 . 익명 객체에 추가 된 멤버는 접근 불가능

class C {
    // Private function, 반환 타입은 익명 객체 타입
    private fun foo() = object {
        val x: String = "x"
    }

    // Public function, 반환 타입은 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

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

Java의 익명 Inner 클래스와 같이 Object 식의 코드는 외부 Scope의 변수에 접근 가능 (Java와 다르게 final 변수에 제한되지 않는다)

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 선언

Kotlin은 (Scala를 따라) Singletone 을 쉽게 선언 가능

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

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

이것을 Object 선언이라고 지칭. 항상 object 키워드 다음에 이름을 가짐

Object를 이름을 직접 사용해서 참조

DataProviderManager.registerDataProvider(...)

슈퍼 타입을 가질 수도 있다.

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

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

주의 : Object 선언은 로컬일 수 없다. (즉 함수내에 바로 중첩도 가능). 하지만, 다른 Object 선언 혹은 비 inner 클래스에 중첩은 가능.

Companion Objects

클래스 내 Object 선언은 companion 키워드를 적용 가능

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

companion 객체의 멤버는 클래스 이름을 한정자로 사용해서 호출 가능

val instance = MyClass.create()

companion 객체 이름을 생략하면 Companion 이 이름으로 사용된다.

class MyClass {
    companion object {
    }
}

val x = MyClass.Companion

companion 객체의 멤버는 다른 언어의 static 멤버처럼 보이지만, 런타임에는 실제 객체의 인스턴스 멤버이다

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


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

단 JVM에서는 @JvmStatic Annotation을 사용해 companion 객체의 멤버를 정적 메소드나 필드로 생성도 가능

자세한 내용 : Java의 상호운용 참조

Object 식과 선언의 의미 차이

중요한 의미 차이 하나가 있다

  • Object 식은 사용될 때 즉시 실행 (및 초기화) 된다
  • Object 선언은 최초 접근시에 연기되어 Lazy 초기화 된다
  • companion 객체는 대응하는 Class가 로드될때 초기화되는데, Java 정적 초기화의 semantics 과 일치한다

results matching ""

    No results matching ""