Object Expressions and Declarations
object의 용도는 어떤 class에서 조금 변경된 객체를 생성할 때, 새로운 subclass의 명시적인 선언 없이 객체를 생성할 수 있다는 점이다.
또한, kotlin에서는 static이 없다. 따라서 kotlin에서는 패키지 내에 함수를 사용하기를 권장한다.
하지만 Companion Object를 이용하여 싱글턴 + Class method를 사용할 수 있다.
객체 표현식
Java에서는 익명 내부 클래스를 사용해서 처리했다. kotlin에서는 object expressions을 이용한다.
//Java
btn.setOnClickListener(new OnClickListener(){
public void onClick(View v){
}
});
//Kotlin
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
}
꼭 슈퍼 타입이 없더라도 객체 표현식은 유용하게 쓸 수 있다.
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
객체 표현식에는 제약 사항도 있다.
익명 객체가 local이나 private으로 선언될 때만 type으로 사용될 수 있다.
익명 객체가 public function이나 public property에서 리턴되는 경우, 익명 객체의 슈퍼 타입으로 동작된다.
이런 경우 익명 객체에 추가된 멤버에 접근이 불가능하다.
class C{
private fun foo() = object { val x: String = "x" }
fun publicFoo() = object { val x: String "y" }
fun bar() {
val x1 = foo.x //works
vla x2 = publicFoo.y // error
}
}
또한 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++
}
})
}
Java와 달리 final 제약조건이 없다.
객체 선언
매우 유용한 Singleton 패턴을 kotlin에서는 object declarations을 이용해서 만들 수 있다.
object DataProviderManager{
fun register DataProvider(provider: DataProvider) {
//...
}
val allDataProviders: Collection<DataProvider>
get() = //...
}
이렇게 만들어진 객체는 object 키워드 뒤에 항상 이름이 있어야 한다.
그리고 할당 구문의 우측에 사용될 수 없다. 참조하려면 해당 이름으로 직접 접근할 수 있다.
또한 상속도 가능하다.
object DefaultListener: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { }
override fun mouseEntered(e: MouseEvent) { }
}
동반자 객체(companion object)
클래스 내부의 object declaration은 companion 키워드를 붙일 수 있다.
class ExClass{
companion object Factory{
fun create(): MyClass = MyClass()
}
}
fun main(args: Array<String>){
val instance = ExClass.create()
}
static이 없는데 class를 객체로 만들지 않고 메서드를 사용하기 위해서는 위와 같이 하면 된다.
객체를 생성하지 않고 클래스의 이름으로 companion object의 메서드를 사용할 수 있게 됐다.
하지만 static과 비슷해 보이지만 같지는 않다.
companion object도 실제 객체의 멤버이다. 또한 슈퍼클래스도 가질 수 있다.
interface Factory<T> {
fun create() : T
}
class MyClass{
companion object: Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}