[Kotlin] Object

코랑·2023년 4월 25일
0

android

목록 보기
8/16

명시적으로 새 하위객체를 만들지 않고 클래스의 일부만 수정한 객체를 만들고 싶을 때 사용함

Object 표현식

표현식은 익명 클래스의 오브젝트를 만듬(class 선언 없이)
한번 사용하기에 유용한 클래스는 선언과 동시에 정의할 수 있고 현재 존재하는 클래스로 상속할 수 있다. interface implement도 가능. 익명 클래스의 인스턴스는 익명 오브젝트라고 불림. 이름이 아니라 표현식으로 정의 되었기 때문에~

처음부터 익명 Object 만들기

중요한 슈퍼타입을 가진 object가 필요한게 아니라면 object뒤에 컬리 브레이스 안에 멤버들을 정의해주면 된다

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

슈퍼타입에서 익명 오브젝트 상속

object 다음에 : 으로 상속할 타입을 정의해주면 된다.
그 다음에 코드블럭에서 implement하거나 override하면 된다

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

    override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
// n개의 supertype 상속 예시 class랑 동일하쥬
open class A(x: Int) {
    public open val y: Int = x
}

interface B { /*...*/ }

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

반환값으로 익명 object 사용하기와 값 타입

익명 오브젝트가 인라인 (함수 혹은 property)선언이 아닌 로컬 혹은 private 타입으로 사용 될 때, 모든 멤버가 함수(혹은 property)를 통해 그 객체 멤버에 접근이 가능하다.

class C {
    private fun getObject() = object {
        val x: String = "x"
    }

    fun printX() {
        println(getObject().x)
    }
}

만약에 함수가 public 혹은 private 인라인이면 그것의 실제 타입은

  • 슈퍼타입이 없으면? 기본적으로 Any
  • 슈퍼타입이 하나만 있으면? 슈퍼타입
  • 슈퍼타입이 여러개면? 명시적으로 정의된 타입

이 모든 케이스에서 추가된 멤버들은 접근 불가능하다. 실제 선언된 타입에 관련된 오버라이드 된 멤버는 접근 가능하다

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

class C {
    // return type: Any, x 접근 불가
    fun getObject() = object {
        val x: String = "x"
    }

    // return type: A; x 접근불가
    fun getObjectA() = object: A {
        override fun funFromA() {}
        val x: String = "x"
    }

    // return type: B, funFromA() and x 접근 불가
    fun getObjectB(): B = object: A, B { // (B같은)명시적인 return type 필요
        override fun funFromA() {}
        val x: String = "x"
    }
}

익명오브젝트에서 변수 접근

오브젝트 표현식 코드는 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 선언

싱글톤

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

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

DataProviderManager.registerDataProvider(...)

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

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

Data object

데이터 오브젝트는 실험적으로 사용하는거라 언제 사라질지 모름~

// 일반 오브젝트 선언+해쉬값
object MyObject

fun main() {
    println(MyObject) // MyObject@1f32e575
}
// 데이터 오브 젝트
data object MyDataObject {
    val x: Int = 3
}

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

데이터 오브잭트 생성 시 컴파일러가 생성하는 함수들(커스텀도 가능함)

  • toString() 데이터 오브젝트 이름 반환
  • equals()/hashCode()

데이터 객체에 대한 equals() 함수는 데이터 객체와 동일한 유형을 가진 모든 객체가 동일한 것으로 간주되도록 합니다. 대부분의 경우 런타임에는 데이터 객체의 인스턴스가 하나만 존재합니다(결국 데이터 객체는 싱글톤). 그러나 런타임에 동일한 유형의 다른 객체가 생성되는 경우(예: java.lang.reflect를 사용한 플랫폼 리플렉션 또는 이 API를 내부적으로 사용하는 JVM 직렬화 라이브러리 사용)에는 객체가 동일한 것으로 취급되도록 합니다.
다른 객체가 생성되는 경우를 이해를 못하겠군,,,

Companion object

클래스 안에 오브젝트 선언할 때 companion 키워드로선언할 수 있음.

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}
// 사용
val instance = MyClass.create()

class MyClass {
    companion object { }
}
// 이름 없이 정의하면 Companion으로 사용할 수 있음.
val x = MyClass.Companion

클래스 멤버는 해당 컴패니언 객체의 비공개 멤버에 액세스할 수 있음
그 자체로 사용되는 클래스 이름은 클래스의 동반 객체에 대한 참조 역할을 합니다(이름 지정 여부에 관계없이)

컴패니언 오브젝트가 다른 언어 관점으로 보면 static 멤버로 보인다.
런타임에서 여전히 실제 오브젝트의 멤버이며, ㅇㅖ를 들면은 인터페이스를 구현할 수도 있음

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

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

val f: Factory<MyClass> = MyClass

@JvmStatic를 사용하면 jvm에서는 진짜 static으로 생성함.

object 표현식과 정의가 의미론적으로 다른점

  • 오브젝트 표현식은 사용 즉시 실행된다
  • 오브젝트 선언은 처음 접근할 때, 늦게 초기화된다.
  • 컴패니언오브젝트는 자바 정적 생성자의 의미와 일치하는 해당 클래스가 로드(리졸브)될 때 초기화된다.

0개의 댓글