App노자

[Kotlin] object 키워드 본문

Android/Kotlin

[Kotlin] object 키워드

앱의노예 2023. 8. 15. 23:54

1. object란?


Kotlin에서는 객체 표현식(object expressions)과 객체 선언(object declarations)으로

새 하위 클래스를 명시적으로 선언하지 않고 일부 클래스를 약간 수정한 객체를 만들 수 있다

object 키워드는 다양한 상황에서 사용하지만  클래스를 정의하면서 동시에 인스턴스(객체)를 생성한다는 공통점이 있다

object 키워드를 크게 다음과 같이 2가지 형태로 사용한다

1. 익명 클래스의 객체를 만들기 위한 객체 표현식 (object expressions)

2. 싱글턴을 정의하기 위해 사용하는 객체 선언 (object declaration)

3. 싱글턴과 정적 멤버를 정의하기 위해 사용하는 객체 선언 (companion object)

 

https://kotlinlang.org/docs/object-declarations.html

 

Object expressions and declarations | Kotlin

 

kotlinlang.org

2. 익명 객체 


fun main(){

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

    println(helloWorld)
}

Object 표현식은 클래스 선언과 함께 명시적으로 선언되지 않은 익명 클래스를 만든다

이러한 클래스는 일회성 사용에 유용하며 처음부터 정의, 기존 클래스에서 상속 혹은 인터페이스를 구현할 수 있다 

익명 클래스의 인스턴스는 이름이 아닌 표현식으로 정의되기 때문에 익명 객체라고도 한다 

익명 객체를 정의할 때는 object 키워드를 작성 후 중괄호 안에 해당 멤버를 작성한다

fun main(){

    val someObject = object: TestInterface {
        override val a: Int = 5

        override fun calculate(): Int {
            return a * a + 12
        }
    }

    println(someObject.calculate())
}

interface TestInterface {
    val a: Int
    fun calculate(): Int
}

object 객체는 상속이 가능하다 

상속하는 object 객체를 만들려면 object 뒤에 상속받을 클래스를 지정한다 

[object: 상속받을 클래스]와 같은 방식으로 사용하며 {} 안에 클래스의 멤버를 구현하거나 재정의한다

binding.testButton.setOnClickListener(object: View.OnClickListener {
    override fun onClick(v: View?) {
        println("Clicked!")
    }
})

상속하는 익명 클래스는 안드로이드 개발을 할 때 자주 사용하는 setOnClickListener에서도 사용되고 있다

object 키워드로 OnClickListener 인터페이스의 구현체를 익명 클래스로 생성하여 setOnClickListener에 넘겨준다

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

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

익명 객체를 반환 값 및 타입으로 사용하는 방법이다

익명 개체가 인라인 선언(함수 또는 속성) 이 아닌 경우에는 이 함수 또는 프로퍼티를 통해 모든 멤버에 액세스 할 수 있다

객체 선언과 달리 무명 객체는 싱글턴이 아니기 때문에 객체 식이 쓰일 때마다 새로운 인스턴스가 계속해서 생성된다

3. 객체 선언 (Singleton pattern) 


object OCustomer {
    var name = "Kildong"
    fun greeting() = println("Hello World!")
    val HOBBY = Hobby("Basketball")
    init {
        println("Init!")
    }
}

class Hobby(val name: String)

fun main() {
    OCustomer.greeting()
    OCustomer.name = "Dooly"
    println("name = ${OCustomer.name}")
    println(OCustomer.HOBBY.name)

}

싱글톤이란 전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며

생성된 객체를 어디에서든지 참조할 수 있도록 하는 디자인 패턴이다

싱글톤을 사용하는 이유는 객체가 서로 동일한 정보를 가질 때 하나의 메모리만 유지해 자원의 낭비를 줄이기 위해서이다

위의 코드와 같이 class가 있어야 할 위치에 object를 입력해 주면 해당 클래스는 싱글턴으로 동작하게 된다

object 선언 방식을 사용하면 접근 시점에 객체가 생성되는데 생성자 호출을 하지 않으므로

주 생성자와 부 생성자를 사용할 수 없지만 초기화 블록인 init이 들어갈 수 있다

object로 선언되면 맴퍼 프로퍼티와 메서드를 객체 생성 없이 점(.) 표기로 바로 사용할 수 있다

4. 동반 객체


class Person {
    var id: Int = 0
    var name: String = "Youngdeok"
    companion object { // 컴페니언 객체의 정의
        var language: String = "Korean"
        fun work() {
            println("working...")
        }
    }
}

fun main() {
    println(Person.language)  // 인스턴스를 생성하지 않고 기본값 사용
    Person.language = "English" // 기본값 변경 가능
    println(Person.language) // 변경된 내용 출력
    Person.work() // 메서드 실행
    //println(Person.name) // name은 companion object가 아니므로 오류
}

변수는 사용 범위에 따라 지역 변수와 전역변수로 나뉜다

지역 변수는 특정 코드 블록 안에 사용되는 변수로서 코드 블록을 벗어나면 프로그램 메모리에서 삭제되고

전역 변수는 프로그램 특정 코드 블록 외부에 있는 변수로서 프로그램이 실행되는 동안 메모리에 유지된다

클래스의 경우는 인스턴스를 생성해 메모리에 동적으로 초기화해서 사용하는데 마찬가지로 클래스에서 사용하는 

프로퍼티나 메서드도 코드의 블록 영역에 따라 사용 범위가 결정된다

이런 동적인 초기화 없이 정적 변수를 사용하기 위해 Java에서는 static키워드를 제공하지만

Kotlin의 경우 companion object를 제공한다

// Kotlin에서 Java의 static 접근
fun main() {
    println(Customer.LEVEL)
    Customer.login()
}


//Java
public class Customer {
    public static final String LEVEL = "BASIC";  // static 필드
    public static void login() { // static 메서드
        System.out.println("Login...");
    }
}

프로젝트에 따라 Java파일과  연동해서 사용해야 할 때가 있다

Kotlin에서의 사용 방식과 크게 차이점이 없어 간단하게 사용이 가능하다

//Java
public class KCustomerAccess {

    public static void main(String[] args) {

        // Kotlin 코드의 KotlinFoo의 멤버를 접근
        System.out.println(KCustomer.LEVEL);
        KCustomer.login(); // 어노테이션을 사용할 때 접근 방법
        KCustomer.Companion.login(); // 위와 동일한 결과로 어노테이션을 사용하지 않을 때 접근 방법

        // KJob에 대한 객체 생성 후 접근
        KJob kjob = KCustomer.JOB;
        System.out.println(kjob.getTitle());

        // KCostomer를 통한 접근
        KCustomer.JOB.setTitle("Accountant");
        System.out.println(KCustomer.JOB.getTitle());
    }
}


//Kotlin
class KCustomer {
    companion object {
        const val LEVEL = "INTERMEDIATE"
        @JvmStatic fun login() = println("Login...") // 어노테이션 표기 사용
        @JvmStatic val score = 3
        @JvmField val JOB = KJob()
    }
}

class KJob {
    var title: String = "Programmer"
}

반대로 Java에서 Kotlin의 companion object에 접근하는 방법이다

애노테이션 표기법을 사용해 Java 소스에서 Kotlin코드를 해석할 수 있게 한다

@JvmStatic: 애노테이션은 Java 소스에서 코드를 해석할 때 companion을 생략할 수 있게 해 준다

@JvmField: 포로퍼티를 Java에서 사용할 경우에 쓰이며 특정 자료형을 사용할 수 있게 해 준다

'Android > Kotlin' 카테고리의 다른 글

[Kotlin] Coroutines (launch, async)  (0) 2023.09.05
[Kotlin] 프로세스와 스레드  (0) 2023.08.20
[Kotlin] Closure (클로저)  (0) 2023.08.10
[Kotlin] 코틀린 표준 라이브러리  (0) 2023.08.09
[Kotlin] by 키워드  (0) 2023.08.06