일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Button
- drift
- LifeCycle
- appbar
- DART
- data
- Navigation
- 앱
- Dialog
- textfield
- 앱바
- TEST
- ScrollView
- tabbar
- 테스트
- Coroutines
- Compose
- Flutter
- android
- textview
- viewmodel
- 계측
- Kotlin
- livedata
- activity
- 안드로이드
- scroll
- intent
- CustomScrollView
- binding
- Today
- Total
Study Record
[Kotlin] object 본문
😶 Object
새 하위 클래스를 명시적으로 선언하지 않고 일부 클래스를 약간 수정한 객체를 만들어야 할 때가 있다. Kotlin 은 Object 선언과 Object expression 으로 이를 처리한다.
😶 Object expression
Object expression 은 클래스 선언으로 명시적으로 선언되지 않은 익명 클래스의 개체를 만든다. 이러한 클래스는 일회용으로 객체 선언과 동시에 정의되거나 기존 클래스에서 상속하거나 인터페이스를 구현할 수 있다. 이렇게 정의된 인스턴스는 클래스 이름을 가지지 않으므로 익명 객체라고도 불린다.
1. 익명 객체 만들기
val helloWorld = object {
val hello = "Hello"
val world = "world"
fun getString(): String = "$hello $world"
override fun toString() = "$hello $world"
}
println(helloWorld.getString())
println(helloWorld.toString())
익명 객체는 생성자(constructor)를 가질 수 없다. init 초기화 블록은 실행된다.
2. 상속받은 익명 객체
익명 객체를 생성할 때 어떤 타입(들)이든 상속받을 수 있다. object 객체 뒤에 ":" 을 붙인 뒤 상속받을 타입을 선언한다. 일반적인 클래스 선언과 비교하면 다음과 같다.
class TestClass() : A() {
/* class Body */
}
val testObject = object : A() {
/* Object Body */
}
Android 에서 자주 사용하는 setOnClickListener 함수를 선언하는 방법과 비슷한 상황을 예시로 들면 다음과 같다. 한 가지 특징이 있다면, 같은 블록 단위에서 실행된 변수를 Object 에서 사용할 수 있다. 밑의 예시로는 count 변수가 있다.
interface OnTestClickListener {
fun onClick(v: TestView?)
}
class TestView {
var onClickListener: OnTestClickListener? = null;
public fun setOnTestClickListener(l: OnTestClickListener) {
println("setOnClickListener setting")
onClickListener = l
}
fun click() {
onClickListener?.onClick(null)
}
}
fun main() {
val view = TestView()
var count = 3
view.setOnTestClickListener(object: OnTestClickListener {
override fun onClick(v: TestView?) {
count++
println("onClick Listener play $count")
}
})
view.click()
}
/* 출력 결과 */
setOnClickListener setting
onClick Listener play 4
익명 객체는 여러개의 타입을 상속받을 수 있다.
open class A(x: Int) {
public open val y: Int = x
}
interface B { /*...*/ }
val ab: A = object : A(1), B {
override val y = 15
}
3. 함수의 리턴값으로 사용되는 익명 객체
class C {
private fun getObject() = object {
val x: String = "x"
}
fun printX() {
println(getObject().x)
}
}
함수의 리턴값으로 익명 객체가 리턴될 때 아무것도 상속받지 않았다면 Any 타입으로 리턴된다. 상속받은 타입이 한개라면 그 타입으로 익명 객체가 리턴되고 여러개면 명시적으로 어떤 타입으로 리턴할지 정의해야 한다.
interface A {
fun funFromA() {}
}
interface B
class C {
// The return type is Any; x is not accessible
fun getObject() = object {
val x: String = "x"
}
// The return type is A; x is not accessible
fun getObjectA() = object: A {
override fun funFromA() {}
val x: String = "x"
}
// The return type is B; funFromA() and x are not accessible
fun getObjectB(): B = object: A, B { // explicit return type is required
override fun funFromA() {}
val x: String = "x"
}
private fun getObjectPrivate() = object {
val x: String = "X"
}
// 맴버 변수 접근 가능
fun playPrintln() {
println(getObjectPrivate().x)
}
}
fun main() {
// 접근 불가능
C().getObject().x
}
또한, Object 의 맴버 변수(x)도 전부 접근할 수 없다. 단, private 키워드가 붙은 함수의 리턴값으로 얻은 Object 객체는 맴버 변수(x)에 접근 가능하다.
😶 Object Declarations
SingleTon 패턴을 사용할 때 Object Declaration 을 사용하면 쉽게 정의할 수 있다. 처음 객체에 접근할 때 Thread-safe 하게 게 선언 초기화된다. 클래스 이름을 타입으로 객체를 따로 선언할 수 없다. 객체에 처음 접근할 때 선언되고 프로젝트 전체에 하나의 객체만 생성되며 접근할 수 있기 때문에 SingleTon 패턴을 고려할 때 유용하게 사용할 수 있다.
object SingleTonObject {
val hello = "Hello"
var count = 3
fun printHello() {
println("$hello $count")
}
}
fun main() {
SingleTonObject.count++
SingleTonObject.printHello()
// 불가능
val singleTonObject = SingleTonObject()
}
Data Object
data class 처럼 Data Object 도 선언이 가능하다. data Class 와는 다른 점은 Data Object 는 SingleTon 객체라는 점이다. 따라서 Data Object 는 인스턴스의 복사본을 만들 수 없어 copy() 함수가 없고 데이터 속성이라는 개념이 없기 때문에 componentN() 함수도 없다.
data object MyDataObject {
val x: Int = 3
}
Companion Object
object Declaraion 은 싱글톤 객체를 정의하는데 사용된다. 이 개념을 클래스 내에서도 사용할 수 있다. 바로 companion 키워드를 앞에 붙여 선언해주면 된다. 만약 따로 이름(ex. Factory)을 짓지 않는다면 Companion 이름으로 사용된다.
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
class MyClass2 {
// 이름이 존재하지 않으므로 Companion 이름 강제 부여
companion object {
fun create(): MyClass2 = MyClass2()
}
}
fun main() {
// 사용할때는 원래의 클래스 이름을 한정자로 사용하여 멤버를 간단히 호출할 수 있다.
val instance = MyClass.create()
}
companion object 를 참조할때는 이름이 존재한다면 이름으로 혹은 원래 클래스 이름을 한정자로 사용하여 맴버를 간단히 호출할 수 있다.
class MyClass1 {
val hello = "hello"
companion object Named {
fun newInstance() = MyClass1()
}
}
class MyClass2 {
val word = "word"
companion object {
fun newInstance() = MyClass2()
}
}
fun main() {
val myClass1 = MyClass.newInstance()
val myNameClass1 = MyClass1.Named.newInstance()
val myClass2 = MyClass2.newInstance()
val myNameClass2 = MyClass2.Companion.newInstance()
}
object Declaraion 은 singleTon 패턴처럼 하나의 객체만 생성된다. 클래스에 object Declaraion 을 선언하는 것이 companion object 이기 때문에 companion object 의 멤버들이 static 처럼 보이지만 런타임 시 실제 객체의 인스턴스 맴버일 때가 있다. 이럴 때 @JvmStatic 어노테이션을 사용하면 static 으로 인식한다.
class MyClass1 {
val hello = "hello"
companion object Named {
@JvmStatic fun newInstance() = MyClass1()
}
}
😶 Object expression 과 Object Declaration 의 차이점
expression 은 사용되는 즉시 실행되고 최기화되지만 declaration 은 첫 번째로 접근되어 사용되어질 때 초기화된다.
'안드로이드 > Kotlin' 카테고리의 다른 글
[Kotlin] coroutines 살펴보기 (0) | 2023.08.08 |
---|---|
[Kotlin] property (0) | 2023.07.21 |
[Kotlin] 람다식 및 고차함수 (0) | 2023.07.19 |
[Kotlin] vararg 키워드 (0) | 2023.07.17 |
[Kotlin] null safety (0) | 2023.07.11 |