Kotlin ‐ 객체[Effective Kotlin Item 12] - woojin-playground/Backend-PlayGround GitHub Wiki
객체
- 코틀린에서는 '객체 표현식'과 '객체 선언'으로도 객체를 만들 수 있다.
객체 표현식
- 표현식으로 빈 객체를 만들려면 object 키워드와 중괄호를 사용한다.
- 객체를 생성하는 이러한 구문을 객체 표현식이라고 한다.
var instance = object {}
- 빈 객체는 어떤 객체도 확장하지 않으며, 인터페이스를 구현하지도 않고, 본문에도 아무 내용이 없다.
- 그럼에도 불구하고 빈 객체는 쓸모가 있다.
- 빈 객체의 의미는 유일성에 있다. 즉, 다른 어떤 객체와도 같지 않은 존재라는 뜻이다.
- 그래서 특정 토큰이나 동기화 락을 사용하기에 적합하다.
class Box {
var value: Any? = NOT_SET
fun initialized() = value != NOT_SET
companion object {
private val NOT_SET = object {}
}
}
private val LOCK = object {}
fun synchronizedOperation() = synchronized(LOCK) {
// ...
}
- 빈 객체는
Any의 생성자로도 만들 수 있기 때문에 object {} 대신 Any()를 사용해도 된다.
private val NOT_SET = Any()
- 하지만 객체 표현식으로는 만드는 객체가 반드시 비어 있을 필요가 없다.
- 본문을 가질 수 있으며 클래스를 상속하거나 인터페이스를 구현할 수도 있다.
- 다만 객체 표현식은 class 대신 object 키워드를 사용하며, 이름이나 생성자를 정의해선 안 된다.
객체 선언
- 객체 표현식에 이름을 부여하면 객체 선언이 된다.
- 객체 표현식과 똑같이 하나의 객체를 생성하지만, 참조할 수 있는 이름이 있기 때문에 익명 객체는 아니라고 볼 수 있다.
object Point {
var x = 0
var y = 0
}
fun main() {
println(Point.x) // 0
Point.y = 10
println(Point.y) // 10
val p = Point
p.x = 20
println(Point.x) // 20
println(Point.y) // 10
}
- 객체 선언은 싱글톤 패턴을 구현한 것으로, 인스턴스가 하나뿐인 클래스를 만든다.
- 이 클래스를 사용할 때마다 오직 이 하나의 인스턴스를 쓰게 된다는 뜻이다.
- 객체 선언은 다른 클래스를 확장하거나 인터페이스를 구현하는 등, 보통의 클래스가 지원하는 기능을 모두 지원한다.
data class User(val name: String)
interface UserProducer {
fun produce(): User
}
object FakeUserProducer : UserProducer {
override fun produce() : User = User("fake")
}
fun setUserProducer(producer: UserProducer) {
println(producer.produce())
}
fun main() {
setUserProducer(FakeUserProducer)
}
컴패니언 객체(companion object)
- 객체 선언 앞에 companion 키워드를 붙이면 객체를 건너뛰고 그 안의 요소들을 곧바로 호출할 수 있다.
class User {
companion object Producer {
fun empty() = User()
}
}
// 사용 예시 코드
val user: User = User.empty()
val user: User = User.Producer.empty()
- 이처럼 companion 키워드가 붙은 객체를 컴페니언 객체라 하며, 객체 이름을 생략할 수 있다.
- 기본 이름은 Companion이다.
class User {
companion object { // 객체 이름은 생략해도 된다.
fun empty() = User()
}
}
데이터 객체 선언
- Kotlin 1.8부터는 객체 선언 시 data 제어자를 사용할 수 있다.
- data 제어자는 객체의 이름을 문자열로 반환하는 toString 메서드를 만들어준다.
data object ABC
fun main() {
println(ABC)
}
상수
- Kotlin에서 상수는 일반적으로 컴패니언 객체의 프로퍼티로 정의하고 이름은
UPPER_SNAKE_CASE처럼 대문자와 밑줄만 사용하여 짓는다.
- 이 방식을 이용하면 상수에 이름을 붙일 수도 있고 추후에 값을 바꾸기도 쉽다.
- 이런 특징적인 방법은 상수임을 명확하게 표시하는 기능도 한다.
class Product(val code: String, val price: Double) {
init {
require(price > MIN_AMOUNT)
}
companion object {
val MIN_AMOUNT = 5.00
}
}
- 컴패니언 객체 프로퍼티나 최상위 프로퍼티가 원시 타입이나 String인 상수라면 const 제어자를 추가할 수 있다. 이는 최적화에 해당된다.
const가 붙은 변수를 사용하는 코드는 컴파일할 때 모두 해당 상수값으로 치환된다.
class Product(val code: String, val price: Double) {
init {
require(price > MIN_AMOUNT)
}
companion object {
const val MIN_AMOUNT = 5.00
}
}