👀학습목표
- 객채 선언의 정의
- 객채 선언의 의미와 객체 선언을 통한 싱글톤
- 자바 익명 클래스와 비슷한 객체 식
📕객체 선언
코틀린은 어떤 클래스에 인스턴스가 오직 하나만 존재하게 보장하는 싱글턴 패턴을 내장하고 있다.
싱글턴의 선언 방법은 클래스와 비슷한 방법으로 선언한다. [class 대신 object라는 키워드 사용]
import java.io.*
object Application {
val name="My application"
override fun toString()=name
fun exit(){}
}
fun describe(app:Application)=app.name //Application은 타입임.
fun main(){
println(Application) //Application은 값임.
}
일반적으로 객체의 인스턴스는 단 하나뿐이므로 인스턴스만 가리켜도 어떤 타입을 쓰는지 충분히 알 수 있다.
따라서 객체를 타입으로 사용해도 무의미하다.
- 클래스와 마찬가지로 객체 선언도 멤버 함수와 프로퍼티를 포함할 수 있고, 초기화 블록도 포함할 수 있다.
- 하지만 객체에는 주생 성자나 부생 성자가 없다. 객체 인스턴스는 항상 암시적으로 만들어지기 때문에 객체의 경우 생성자 호출이 아무런 의미가 없다.
- 객체의 본문에 들어있는 클래스에는 inner 사용을 금지한다.
- 최상위 선언들과 마찬가지로, 객체의 멤버를 임포트 해서 간단한 이름만 사용해 다른 파일에서 참조할 수 있다.
- ex) import Application.exit //success
- 하지만 객체의 모든 멤버가 필요할 때 임포트 문으로 임포트 할 수는 없다.
- ex) import Application.* //error
- 객체 정의 안에는 다른 클래스 정의와 같이 toString()이나 equals() 같은 공통 메서드 정의가 들어있기 때문에 사용하지도 않을 공통 메서드까지 임포트 돼 문제가 생길 수 있기에 제약을 걸어둔다.
📕동반 객체
- priavte로 생성자를 지정해 클래스로 외부에서 사용할 수 없게 한 다음 내포된 객체를 만듬.
- companion이라는 키워드를 덧붙인 내포된 객체.
- 동반 객체의 멤버에 접근할 때는 동반 객체의 이름을 사용하지 않고 동반 객체가 들어있는 외부 클래스의 이름을 사용할 수 있다.
- 정의에서 이름을 아예 생략할 수 있다. 아래 코드에서는 동반 객체의 이름은 Factory이다.
- 만약 생략한 경우 동반 객체의 디폴트 이름은 Companion으로 가정한다.
- 동반 객체의 멤버를 임포트 하고 싶을 때는 객체 이름을 명시해야 한다.
- import Application.create //error
- import Application.Companion.create //ok
- 한 클래스에서 동반 객체가 두 개 이상일 수는 없다.
import java.io.*
class Application private constructor(val name : String){
companion object Factory{
fun create(args: String) : Application?{
val name= args?: return null
return Application(name)
}
}
}
fun main()
{
val str=readLine()!!.toString()
val app=Application.create(str)?:return
println("Application started : ${app.name}")
}
📕객체 식
코틀린은 명시적인 선언 없이 객체를 바로 생성할 수 있는 객체식을 제공한다. 객체 식은 자바 익명 클래스와 아주 비슷하다.
fun main(){
fun midPoint(xRange : IntRange, yRange:IntRange)= object{
val x= (xRange.first + xRange.last)/2
val y= (yRange.first+ yRange.last)/2
}
val midPoint= midPoint(1..5,2..6)
println("${midPoint.x},${midPoint.y}")
}
위 코드를 보면 알 수 있듯이 객체 식은 이름이 없는 객체 정의처럼 보입니다.
그리고 코틀린에서 처럼 객체 식도 식이므로, 예제처럼 객체 식이 만들어내는 값을 변수에 대입할 수 있습니다. 클래스나 객체 식과 달리 객체를 함수 안에 정의할 수는 없습니다.
fun printMiddle(xRange : IntRange, yRange : IntRange){
object MidPoint{
val x= (xRange.first+xRange.last)/2
val y=(yRange.first+yRange.last)/2
}
println("${MidPoint.x},${MidPoint.y}")
}
객체를 함수 안에 정의할 수 없도록 결정한 이유는 객체 선언이 싱글턴을 표현하지만, 지역 객체들은 자신을 둘러싼 바깥 함수가 호출될 때마다 매번 다시 생성돼야 하기 때문입니다.
fun MidPoint()의 반환 타입은 객체 식 안에 정의된 모든 멤버가 들어있는 클래스를 표현하는 익명 객체 타입(anonymous object type)이며, 단 하나만 존재하는 타입이다.
💡즉 똑같은 두 객체 식이 있다고 가정했을 때, 두 개의 익명 개체 타입은 다른 겁니다!
객체 식이 만들어내는 객체고 다른 클래스 인스턴스와 마찬가지로 사용할 수 있습니다.
fun main(){
val o= object{ //익명 개체 타입이 추론됨.
val x=readLine()!!.toInt()
val y = readLine()!!.toInt()
}
println(o.x + o.y) //객체 밖에서 객체 타입 인스턴스에 접근이 가능함.
}
하지만 익명 객체 타입은 지역 선언이나 비공개 선언(private)에만 전달될 수 있습니다.
아래 코드는 midPoint라는 함수를 최상위 함수로 정의한 코드입니다.
fun midPoint(xRange: IntRange, yRange:IntRange)=object{
val x= (xRange.first+xRange.last)/2
val y=(yRange.first+yRange.last)/2
}
fun main(){
val midPoint(1..5, 2..6)
println("${midPoint.x},${midPoint.y}") //객체 멤버에 접근할때 오류가 발생합니다
}
여기서 midPoint 함수의 타입은 익명 객체 타입이 아니라 Any타입이 됩니다. 그래서 midPoint.x 참조에서 x를 찾을 수 없는 것입니다.
✔정리
- 코틀린은 싱글턴 패턴(인스턴스가 단 하나만 존재하는 클래스)을 내장하고 있다.
- 객체 안에 정의된 클래스에 대해서는 inner 사용을 금지한다. -> 항상 인스턴스가 하나뿐이므로, 불필요함.
- 동반 객체는 팩토리 디자인 패턴을 쉽게 구현하는 경우 유용하게 활용할 수 있다.
- 선언은 companion이라는 키워드를 붙여준다.
- 객체의 멤버를 임포트 해서 사용할 수 있지만, 모든 멤버가 필요할 때는 임포트 문으로 임포트 할 수 없다.
- 객체 정의 안에 toString(), equals()등 공통 메서드 정의까지 불 필요하게 임포트 돼 문제가 생길 수 있기 때문이다.
- 코틀린은 명시적인 선언 없이 객체를 바로 생성할 수 있는 객체식을 제공한다.
- 객체 식은 기존 클래스의 하위 클래스를 선언하지 않고도 기존 클래스를 약간만 변경해 기술하는 간결한 방법을 제공한다.
'Skils > Kotlin' 카테고리의 다른 글
[Kotlin] - 코루틴 (0) | 2023.03.05 |
---|---|
[Kotlin] 고차 함수와 함수타입 (0) | 2022.09.14 |
[Kotlin] 프로퍼티(Property) (0) | 2022.09.02 |
[Kotlin] 널 가능성 (0) | 2022.09.01 |
[Kotlin] 멤버 가시성, 내포된 클래스(inner), 지역 클래스 (0) | 2022.08.30 |