Search
Duplicate

[Kotlin] by 키워드

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Kotlin
Android
Scrap
태그
위임
9 more properties
안드로이드 코드를 짜면서 by lazyby viewModels()를 쓸 때 by라는 키워드를 사용했는데 이것이 어떤 역할을 하는지 작성해보려고 한다.

위임 패턴

객체가 요청을 다른 객체(helper object)에 위임해서 처리하는 패턴이다.
class Rectangle(val width: Int, val height: Int) { fun area() = width * height } class Window(val bounds: Rectangle) { //위임 fun area() = bounds.area() }
Kotlin
복사
여기서 Window 클래스는 area 메서드로 호출되는 요청을 인자로 갖고 있는 Rectangle 객체에 위임하고 있다.

클래스 위임

interface ClosedShape { fun area(): Int } class Rectangle(val width: Int, val height: Int) : ClosedShape { override fun area() = width * height } class Circle(val radius: Int) : ClosedShape { override fun area() = radius * radius * 3 } class Window(private val bounds: ClosedShape) : ClosedShape by bounds fun main() { val circleWindow = Window(Circle(5)) val rectangleWindow = Window(Rectangle(4, 5)) println(circleWindow.area()) //prints 75 println(rectangleWindow.area()) //prints 20 }
Kotlin
복사
코틀린에서는 by키워드를 사용하여 언어 차원에서 위임 패턴을 구현할 수 있다. Window클래스는 ClosedShape를 구현하며, by키워드로 인해 인자로 전달된 bounds가 구현한 override메서드들을 사용할 수 있게 된다.

프로퍼티 위임

import kotlin.reflect.KProperty class Example { var p: String by Delegate() } class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name}' in $thisRef.") } }
Kotlin
복사
프로퍼티 위임은 프로퍼티의 Accessor(getset)을 delegate에 위임하는 것이다. 위의 코드에서 프로퍼티p:String은 Delegate에 getValue와 setValue를 이임하고, Delegate클래스 내에 각각 오버로드돼있다.
val example = Example() println(example.p) // prints Example@33a17727, thank you for delegating 'p' to me! example.p = "NEW" // prints NEW has been assigned to 'p' in Example@33a17727.
Kotlin
복사
p를 읽으면 get이 Delegate에 위임돼있으므로 getValue메서드가 호출된다. p를 수정하면 set이 Delegate에 위임돼있으므로 setValue메서드가 호출된다. 각각 내부의 String이 리턴돼 위 코드의 주석처럼 출력된다.

by lazy

lazy()메서드는 람다를 인자로 받아 Lazy<T>인스턴스를 반환한다. get()의 첫 호출은 lazy()에 전달된 람다를 기억하고 그 후의 get()호출은 기억한 값을 그대로 반환한다.
val lazyValue: String by lazy { println("computed!") "Hello" } fun main() { println(lazyValue) println(lazyValue) } // computed! // Hello // Hello
Kotlin
복사
첫 println(lazyValue)는 lazy에 전달된 람다를 호출하여 computed가 같이 출력되고 "Hello"를 저장하고, 다음 println(lazyValue)는 저장된 값을 그대로 반환하여 "Hello"만 반환한다.

by viewModels()

val someViewModel: SomeViewModel by viewModels()
Kotlin
복사
프로퍼티 someViewModel은 SomeViewModel타입이며, Accessor를 viewModels()에 위임한다.
/* ActivityViewModelLazy.kt */ @MainThread inline fun <reified VM : ViewModel> ComponentActivity.viewModels( factory: ViewModelProvider.Factory? = null ): Lazy<VM> = ActivityViewModelLazy(this, VM::class, factory) /** * An implementation of [Lazy] used by [ComponentActivity.viewModels] tied to the given [activity], * [viewModelClass], [factory] */ class ActivityViewModelLazy<VM : ViewModel>( private val activity: ComponentActivity, private val viewModelClass: KClass<VM>, private val factory: ViewModelProvider.Factory? ) : Lazy<VM> { private var cached: VM? = null override val value: VM get() { var viewModel = cached if (viewModel == null) { val application = activity.application ?: throw IllegalArgumentException("ViewModel can be accessed " + "only when Activity is attached") val resolvedFactory = factory ?: AndroidViewModelFactory.getInstance(application) viewModel = ViewModelProvider(activity, resolvedFactory).get(viewModelClass.java) cached = viewModel } return viewModel } override fun isInitialized() = cached != null }
Kotlin
복사
ActivityViewModelLazy는 Lazy를 구현하여 비슷하게 처음 접근했을 때 ViewModelProvider에서 ViewModel을 가져오며 그 이후에 접근했을 때는 cached에 저장된 ViewModel을 반환한다.