코틀린 객체
코틀린의 객체 선언은 클래스와 상수를 합한 것으로 객체 선언을 통해 싱글톤(singleton: 인스턴스가 단 하나 존재하는 클래스)을 만들 수 있습니다.
객체 선언
코틀린의 객체 선언은 class 대신 object를 사용합니다. 객체 선언은 클래스를 정의하는 동시에 인스턴스를 정의한다고 보면 됩니다. 객체 정의는 Thread-Safe하기 때문에 컴파일러는 실행되는 여러 스레드에 대해 하나의 인스턴스만 공유되고 초기화 코드 역시 한 번만 실행되도록 보장합니다.
초기화 작업은 싱글톤 클래스가 실제 로딩되는 시점까지 지연되며, 보통 프로그램이 해당 객체 인스턴스에 처음 접근할 때 초기화됩니다.
자바에서는 비공개 생성자와 정적(static) 상태를 조합한 클래스 정의를 통해 싱글톤을 만들고, 아래 예시처럼 같은 싱글톤을 생성하더라도 코틀린과 달리 많은 코드가 필요합니다.
object Application {
val name = "My Application"
override fun toString() = name
fun exit() {}
}
public final class Application {
private static final String name = "My Application";
public static final Application INSTANCE;
private Application() {}
public final String getName() { return name; }
public final void exit() {}
static {
INSTANCE = new Application();
name = "My Application";
}
}
코틀린의 객체 선언은 일반적인 클래스 선언과 동일하게 멤버 함수와 프로퍼티 및 초기화 블록(init)을 포함할 수 있습니다. 하지만, 클래스와 달리 객체에는 주생성자나 부생성자가 없습니다. 객체 인스턴스는 항상 암시적으로 만들어지므로 객체의 생성자 호출은 의미가 없습니다.
동반 객체
동반 객체는 companion이라는 키워드를 덧붙인 내포된 객체입니다. 동반 객체 멤버에 접근할 때는 동반 객체의 이름을 사용하지 않고 동반 객체가 속한 외부 클래스의 이름을 사용할 수 있습니다.
class TestApplication private constructor(val name: String) {
object Factory {
fun create(args: Array<String>): TestApplication? {
val name = args.firstOrNull() ?: return null
return TestApplication(name)
}
}
}
fun main(args: Array<String>) {
// 직접 생성자를 호출할 수 없음
val testApplication = TestApplication.Factory.create(args) ?: return
println("testApplication.name: ${testApplication.name}")
}
- 위 Factory는 클래스에 내포된 객체로, 해당 클래스의 인스턴스가 생기면 자신을 둘러싼 클래스의 비공개 멤버에 접근이 가능합니다.
- 팩토리 디자인 패턴 구현 시 유용(생성자를 직접 호출하지 않고자 할 때 사용)
- 생성자는 항상 자신이 정의된 클래스의 객체를 반환하거나 예외만을 던질 수 있습니다. 하지만, 위처럼 내포 객체를 사용하면, 생성자를 비공개로 하면서 해당 객체를 통해 자신이 정의된 클래스의 객체를 생성할 수도, 그 외 작업을 수행할 수도 있습니다.
- 위 예시의 경우, 첫번째 인자가 비어있다면, 내포 객체가 속한 클래스의 객체를 생성하지 않고 null을 반환합니다.
이때, import TestApplication.Factory.create를 하지 않는 이상 위처럼 항상 매번 내포된 객체의 이름을 지정해주어야 합니다. 코틀린에서는 위와 같은 Factory 메소드를 동반 객체(companion object)로 정의하여, 내포 객체를 좀 더 쉽게 사용할 수 있게 해줍니다.
class TestApplication private constructor(val name: String) {
companion object { // companion object Factory로도 사용 가능하지만 Factory 생략 가능
fun create(args: Array<String>): TestApplication? {
val name = args.firstOrNull() ?: return null
return TestApplication(name)
}
}
}
fun main(args: Array<String>) {
// 직접 생성자를 호출할 수 없음
val testApplication = TestApplication.create(args) ?: return
println("testApplication.name: ${testApplication.name}")
}
- 내포 객체 이름을 넣지 않고 클래스명.내포 객체의 메소드명으로 동반 객체 멤버 호출
코틀린의 동반 객체는 자바의 정적(static) 문법과 대응하는 것으로 보일 수 있습니다. 자바의 정적 멤버와 마찬가지로 코틀린의 동반 객체의 멤버도 외부 클래스와 동일한 전역 상태를 공유하며, 외부 클래스의 모든 멤버에 멤버 가시성과 무관하게 접근이 가능합니다.
다만, 코틀린 동반 객체가 자바의 정적 문법보다 더 유연합니다(이 부분은 추후 알아볼 예정입니다).
객체 식
코틀린은 명시적인 선언 없이 객체를 바로 생성할 수 있는 식을 제공합니다. 객체 식(object expression)은 자바 익명 클래스(anonymous class)와 비슷합니다.
fun main() {
fun midPoint(xRange: IntRange, yRange: IntRange) = object {
val x = (xRange.first + xRange.last)/2
val y = (yRange.first + yRange.last)/2
}
val midPoint = midPoint(1..5, 2..6)
println("${midPoint.x}, ${midPoint.y}")
}
- 객체 식은 이름 없는 객체 정의처럼 보입니다.
참고 자료
'PROGRAMMING LANGUAGE > KOTLIN' 카테고리의 다른 글
[Kotlin] Collections의 Iterable과 Sequences 차이점 (0) | 2023.08.23 |
---|---|
[Kotlin]enum class와 data class (0) | 2022.08.25 |
[Kotlin] 널 가능성 (0) | 2022.08.12 |
[Kotlin] 클래스 기초 (0) | 2022.08.12 |
[Kotlin] 코틀린 함수 (0) | 2022.08.04 |