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 과 일치한다