10. 오브젝트 키워드 - Object Expressions and Declarations
1. 오브젝트 - Object
👉 Object 용도
- 어떤 class에서 조금 변경된 객체를 생성 할 때
- 새로운 subclass의 명시적인 선언 없이 객체 생성
👉 3가지 요소.
- Object Expressions
- Java익명 객체
- Object Declarations
- 싱글턴
- Compaion Object
- 싱글턴 + Class method (static 팩토리메서드.. 등과 같은개념)
- 코틀린에서는 정적(static) 변수 혹은 메소드가 없다→ 대신 패키지 내에 함수를 선언하여 사용할 수 있다.
- → 코틀린에서 권고하는것은 같은 패키지레벨에서 함수를 써라.
2. 객체 표현식 (Object Expressions)
- java에서는 익명 내부 클래스를 사용하여 처리하였다. 단, 코틀린에서는 Object expressions를 사용한다.
// java
btn.setOnclickListener( new OnclickListstener() {
public void onClick(View v) {
//...
}
}
//kotlin
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent { ... }
override fun mouseEntered(e: MouseEvent { ... }
})
위처럼 익명 클래스(new) 를 사용하지않고, object 키워드를 이용하여 구현한다.
이해하기 쉬운 예시
class MyRun : Runnable {
override fun run() {
println("hello ~ ")
}
}
fun main(args: Array<String>) {
val t = Thread(MyRun())
t.start()
}
//콘솔 출력 : hello ~
위에서 보면,
val t = Thread(MyRun()) 할때 MyRun 클래스를 정의하지 않고 그냥 Runnable의 run()만 조금 바꿔서 만들 고 싶을때가 있다.
아래와 같이 하면된다.
class MyRun : Runnable {
override fun run() {
println("hello ~ ")
}
}
fun main(args: Array<String>) {
val t = Thread(object : Runnable {
override fun run() {
println("hello ~ ")
}
})
t.start()
}
//콘솔 출력 : hello ~
MyRun()을 제거하고 위와같이 object 명령어를 쓰고 재정의하면 된다.
람다식을 사용하면 더 간단해진다
fun main(args: Array<String>) {
val t = Thread(Runnable {
println("hello ~ ")
})
t.start()
}
//콘솔 출력 : hello ~
//Runnable 도 지워도 됨.
2-1. 객체 표현식 상속
- 슈퍼타입의 생성자가 있는 경우, 반드시 값을 전달 해 주어야 함
- 슈퍼타입이 여러 개인 경우 콜론(:) 뒤에 콤마(,) 로 구분해서 명시 해주면 됨.
open class A(x: Int) {
public open val y: Int = x
}
interface B { ... }
val ab: A = object : A(1), B { //A에 반드시 값을 전달하였고, 슈퍼타입이 여러개 쓸것이니 콤마를 씀
override val y = 15
}
2-2. 객체 표현식 상속 없는 경우
- 특별히 상속받은 슈퍼타입이 없는 경우, 간단하게 작성이 가능하다
fun main(args: Array<String>) {
val data = object {
var x = 100
var y = 200
}
println("${data.x}, ${data.y}")
}
// 콘솔출력 : 100, 200
- 아무것도 없어도된다. 단 바디는 있어야된다.
fun main(args: Array<String>) {
val data = object { }
println(data)
}
2-3. 객체 표현식 제약 사항
- 익명객체가 local이나 private로 선언될 때만 type으로 사용 될 수 있다.
- 익명객체가 public funcion이나 public property에서 리턴 되는 경우에는 익명객체의 슈퍼타입으로 동작된다. 이런 경우에는 익명객체에 추가된 멤버에 접근이 불가능하다.
class C {
private fun foo() = object { val x: String = "X" }
fun publicFoo() = object { val x: String = "X" }
fun bar() {
val x1 = foo().x // 가능
val x2 = publicFoo().x // error
}
}
2-4. 객체 표현식 특징
- 익명객체의 코드는 enclosiong scope의 변수를 접근 할 수 있음
- java와 다르게 final variables 제약조건 없다.
fun count(...) {
var count = 0
clickClass.addClick(object: myClick() {
override fun click() {
count++ // count에 접근이 가능!!
}
})
}
3. 객체 선언 (Object declarations)
- 객체선언 용도싱글턴 패턴을 코틀린에서는 object declarations를 이용하여 만들 수 있다.
object DataProvicderManager {
fun registDataProvider(provider: DataProvider) {
//...
}
val allDataProviders: Collection<DataProvider>
get() = //....
}
3-1. 객체 선언 문법
- object 키워드 뒤에 항상 이름이 있어야함
- object declaration(객체선언)은 object expression(객체표현식)이 아니므로, 할당 구문의 우측에 사용 될 수 없다.
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) { }
val allDataProviders: Collection<DataProvider>
}
- object declaration 의 객체를 참조하려면, 해당 이름으로 직접접근하면 된다.
DataProviderManager.registerDataProvider(...)
쉬운설명
object CountManager {
var count = 0
}
fun main(args: Array<String>) {
CountManager.count++
println(CountManager.count) //1
CountManager.count++
println(CountManager.count) //2
CountManager.count++
println(CountManager.count) //3
}
- 슈퍼타입을 가질 수 있음 (상속가능)
object 이름: 상속객체() {
// ...
}
4. 동반자 객체 (Companion Object)
- 클래스 내부의 object declaration은 companion 키워드를 붙일 수 있다.
- companion object의 멤버 클래스 이름을 통해서 호출 할 수 있다
class MyClass {
//companion만 빼면 객체선언식이나 다름없다.
companion object Factory {
fun create(): MyClass = MyClass()
}
}
//MyClass.Factory.create()로 호출해야되는데, companion object가 되면 바로 호출가능
//자바의 static 메서드를 커버할 수 있다.
//멤버 클래스 이름을 통해서 호출
val instance = MyClass.create()
- companion object의 이름은 생략 할 수 있다.
- 생략할 경우 [class name].Companion으로 객체의 접근이 가능하다
class MyClass {
companion object {
//...
}
}
val x = MyClass.Companion
5. 차이점 & 특징 정리.
- Object Expressions : 즉시 초기화 되고 실행된다.
- Object Declarations : 나중에 초기화 된다.(최초 접근시)
- 처음로드 될때 생성되는게 아니기때문에 맘놓고 사용
- 여러번 호출해도 최초 접근할때 생성된거 1개만 생성 → 싱글턴
- Companion Object : 클래스가 로드 될 때 초기화 된다. java staitc initializer와 같다.