object 키워드가 사용되는 경우인, 다음 내용을 정리한다.
객체 선언 : 싱글턴 객체 생성
인스턴스가 하나만 필요한 경우가 있다.
자바에서는 다음과 같이 singleton pattern 으로 구현할 수 있다.
- 클래스의 생성자를 private 으로 제한하고,
- 정적인 플드에 그 클래스의 유일핸 객체를 저장
코틀린은 ‘객체 선언’ 기능을 통해, 싱글턴 패턴을 언어에서 기본 지원한다.
( 객체 선언 = 클래스 선언 + 그 클래스에 속한 단일 인스턴스 선언 )
object keyword 를 사용해서,
클래스를 정의하고 그 클래스의 인스턴스를 만들어서 변수에 저장하는 모든 작업을 한 문장으로 처리한다.
일반 클래스의 객체와 달리, 싱글턴 객체는 객체 선언문이 있는 위치에서 생성자 호출없이 즉시 만들어진다.
1 2 3 4 5 6 7 8 9 10 11 12 13
| object Payroll { val allEmployees = arrayListOf<Person>()
fun calculateSalary() { for (person in allEmployees) { } } }
Payroll.allEmployees.add(Person(name = "jko", isMarried = false)) Payroll.calculateSalary()
|
클래스 안에 객체로 선언할 수도 있다.
1 2 3 4 5 6 7 8 9 10
| data class Person(val name: String) {
object NameComparator : Comparator<Person> { override fun compare(o1: Person, o2: Person): Int = o1.name.compareTo(o2.name) } }
val persons = listOf(Person("jko"), Person("junhee-ko")) println(persons.sortedWith(Person.NameComparator))
|
동반 객체 : 팩토리 메서드와 정적 멤버가 들어갈 장소
코틀린은, static 키워드를 지원하지 않는다. 대신, top-level function 을 지원한다.
하지만, top-level function 은 클래스의 비공개(private) 멤버에 접근할 수 없다.
그래서, 클래스의 객체와 관계없이 호출하며 클래스의 내부 정보에 접근해야하는 함수가 필요할 때는
클래스에 중첩된 객체 선언의 멤버 함수로 정의해야한다.
생성자를 통해 User 객체를 만들 수 없고 팩토리 메서드를 이용해야하는 예를 보자.
1 2 3 4 5 6 7 8 9 10 11
| class User private constructor(val nickName: String) {
companion object { fun newSubscribingUser(email: String) = User(email.substringBefore('@')) fun newFacebookUser(accountId: Int) = User("test:accountId") } }
val subscribingUser = User.newSubscribingUser("junheee.ko@gmail.com") val facebookUser = User.newFacebookUser(1)
|
동반 객체도 결국, 클래스 안에 정의된 일반 객체이다. 그래서,
- 이름을 붙이거나
- 인터페이스를 상속하거나
- 동반 객체 안에 확장 함수와 프로퍼티를 정의할 수 있다.
동반 객체에 이름 붙이기
1 2 3 4 5 6 7 8 9 10
| class Person(val name: String) {
companion object Loader { fun fromJson(jsonText: String): Person = Person("$jsonText") } }
println(Person.Loader.fromJson("{name: 'jko'}")) println(Person.fromJson("{name: 'junhee-ko'}"))
|
동반 객체의 인터페이스 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| interface JsonFactory<T>{ fun fromJson(jsonText: String): T }
class Person(val name: String) {
companion object : JsonFactory<Person> { override fun fromJson(jsonText: String): Person = Person("$jsonText") } }
fun loadFromJSON<T>(factory: JSONFactory<T>): T { }
loadFromJSON(Person)
|
동반 객체의 확장
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Person(val name: String) {
companion object {
} }
fun Person.Companion.fromJson(json: String): Person = Person("$json")
val json = "..." val p = Person.fromJson(json)
|
fromJson 은 클래스 밖에 정의한 확장 함수이다. 그런데, 동반 객체 안에 정의한 것 처럼 호출 할 수 있다.
객체 식 : 무병 내부 클래스를 다른 방식으로 작성
anonymous object (무명 객체) 를 정의할 때 object 키워드를 사용할 수 있다.
무명 객체는 자바의 무명 내부 클래스를 대신한다.
자바의 무명 내부 클래스로 많이 구현하는 event listener 를 코틀린으로 구현해보자.
1 2 3 4 5 6 7 8 9 10 11
| window.addMouseListener( object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { }
override fun mouseEntered(e: MouuseEvent){ } } )
|
객체에 이름을 붙여야한다면, 변수메 무명 객체를 대입하면 된다.
1 2 3 4 5 6 7 8 9
| val listener = object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { }
override fun mouseEntered(e: MouuseEvent){ } }
|
자바와 달리 final 이 아닌 변수도 객체 식 안에서 사용 가능하다.
1 2 3 4 5 6 7 8 9 10 11
| fun countClicks(window: Window) { var clickCount = 0
window.addMouseListener( object : MouseAdapter() { override fun mouseClicked(e: MouseEvent?) { clickCount++ } } ) }
|
Kotlin In Action <드미트리 제메로프, 스베트라나 이사코바>