[kotlin] Extension

neoneoneo·2024년 4월 9일
0

kotlin

목록 보기
48/49

Extension 무엇이며 왜 쓰는가?

Extension이란 기존의 클래스나 데이터 타입에 새로운 기능을 추가하거나 확장하는 기술을 말한다.

주로 기능 확장, 인터페이스 구현, 라이브러리 확장, 안드로이드 프레임워크 확장, 안드로이드 스튜디오 플러그인 확장이 필요할 때 쓰인다고 한다..!

정말 코딩판은 틀에 박인 사고를 할 수 없는 환경인 것 같다.. 이거 라이브러리에서 주는건 좀 빈약해서 안될 것 같은데...는 안먹힌다. 만들어서 쓰면 되니까^^.. 부족하면 만들어서 먹으라구 extension이라는 기능도 제공하니까^^.. 개발자들.. 대다내..

무튼 이번 글에서는 캠프에서 배운 extension의 예시를 다시 살펴보고, 각각 쓰인 키워드에 대해서 조금 더 심도있게 이해해보는 내용을 작성해보려고 한다.

예시

에 들어가기 전 사전 지식

inline

  • 함수를 호출하는 곳에서 본문을 코드의 일부로 삽입(인라인)하여 실행되도록 한다.
    • 함수 호출로 인한 추가적인 스택 프레임 생성 방지

reified

  • inline 함수 내에서 사용되며, 이 함수가 실행될 때 컴파일러에게 해당 타입을 알려준다.

T

  • 제네릭 타입의 매개변수로, 특정한 타입을 미리 지정하는 것이 아닌 개발자가 원하는 타입을 지정할 수 있게끔 하려면 T를 사용한다.

Any

  • kotlin의 최상위 클래스로, 모~든 클래스의 부모 클래스이다. 즉, 모든 객체의 기본 타입이 되겠다.

vararg

  • 가변 인자를 나타내는 키워드로, 함수의 매개변수로 여러 개의 인자를 받을 수 있도록 해준다.
    • 이를 사용하여 인자를 전달하면 함수 내부에서는 인자가 배열로 처리된다.
    • vararg pair: Pair<String, Any?>라고 작성했다고 하면, 여러 개의 Pair<String, Any?> 인자를 받을 수 있게 된다.

by

  • 속성 위임을 구현할 때 사용되는 키워드로, 속성 위임이란 클래스의 속성을 다른 객체에 위임하여 그 객체가 해당 속성의 동작을 정의하고 제어하도록 하는 것이다.
    • 속성을 위임하면, 위임 받은 객체는 속성 대신 초기화하고 사용할 수 있다. 좀 이해하기 어려운 개념인 것 같다.. 대략적으로는 by OOO 하므로써 OOO를 상속 받거나 하지 않고 바로 OOO에 정의된 함수나 프로퍼티를 사용할 수 있다는 것으로 이해하고 넘어가본다..!

데이터 보내기

데이터를 특정 액티비티로 보낼 때 아래와 같이 작성한다.

private fun adapterOnClick(flower: Flower) {
	val intent = Intent(this, FlowerDetailActivity()::class.java)
    intent.putExtra(FlowerDetailActivity.EXTRA_FLOWER, flower.id)
    startActivity(intent)
}

"만약" 액티비티 간 데이터 전환이 수십번 일어난다면? 매번 저 코드 3줄을 적어주는 것 보다는 extension으로 저 3줄에 대한 행위를 미리 정의해두고, 필요한 액티비티에서 꺼내다가 쓸 수 있도록 할 수 있다.

그럼 저 부분을 모든 액티비티에서 사용을 할 수 있게끔 extension으로 뽑아보자.

//intent.kt
inline fun <reified T: Any> newIntent(context: Context) : Intent = Intent(context, T::class.java)
  • 새로운 인텐트를 만드는 부분
  • 함수의 이름은 newIntent이고, 저 이름으로 호출하여 사용하면 된다.
    • <reified T: Any>newIntent<여기에> 제네릭 타입이 들어올 수 있게 된다.
  • 인텐트를 생성할 때 T::class.java로 해당하는 클래스를 가져올 수 있다.
//intent.kt
inline fun <reified T : Any> Context.launchActivity(vararg pair: Pair<String, Any?>) {
	val intent = newIntent<T>(this)
    intent.putExtra(bundleOf(*pair))
    startActivity(intent)
}
  • 만들어진 인텐트를 사용하여 데이터를 넘기고 액티비티를 시작하는 부분
  • 위에서 만든 newIntent로 새로운 인텐트를 생성한다.
  • newIntent<T>(this) 부분을 보면 context 인자만 넣어서 넘기고 있는데, T로 실행 중인 클래스를 알 수 있기 때문에 뒷 부분의 인자까지 작성하는 것은 의미가 없고 문법적으로 유효하지 않다고 한다.
  • vararg 키워드를 사용하여 여러 개의 Pair<String, Any?> 형태 인자가 들어올 수 있음에 대해 명시해둔다.

extension 사용 전 코드

private fun adapterOnClick(flower: Flower) {
	val intent = Intent(this, FlowerDetailActivity()::class.java)
    intent.putExtra(FlowerDetailActivity.EXTRA_FLOWER, flower.id)
    startActivity(intent)
}

extension 사용 후 코드

private fun adapterOnClick(flower: Flower) {
	launchActivity<FlowerDetailActivity>(FlowerDetailActivity.EXTRA_FLOWER to flower.id)
}

데이터 받기

데이터를 보냈다면 받는 것도 필요할 것이다.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
  	val value = intent.extras?.getLong(EXTRA_FLOWER)
    val flower = value?.let { DataSource().getFlowerForId(it) }
}

이 부분을 extension으로 뽑아보자.

//intent.kt
inline fun <reified T : Any> Activity.extraNotNull(key : String, defalut : T? = null) = lazy {
	val value = intent?.extras?.getLong(key)
  	requireNotNull(if(value is T) value else default) { key }
}
  • 데이터를 받아오는 부분
  • Activity. 하고 extraNotNull 이라는 함수명을 정의해주었으므로 이 함수는 액티비티 클래스의 확장 함수이고 액티비티 인스턴스를 통해 사용할 수 있다.
  • key: String 는 데이터에서 값을 가져오기 위한 키 값을 의미한다.
  • defult: T? = null는 기본 값으로 사용할 수 있는 타입에 대한 정의이다. 만약 함수 호출 시 두 번째 매개변수 값이 비어있다면 기본 값으로 null이 사용된다.
  • requirtNotNull(...) {}제공된 값이 null이라면 exception을 발생 시키는데, () 안에 if (value is T) value else default가 들어가 있으므로 들어온 값이 제네릭 타입이면 해당 값을 반환, 그렇지 않다면 default를 반환한다.
    • 결국 이 부분은 value가 제네릭 타입이 아니거나 null일 경우에는 exception이 발생되고, 그렇지 않다면 value를 그대로 반환한는 부분이다. (value를 검사하는 부분)
    • { key }를 두어, 예외가 발생하면 해당 키 값이 메세지로 출력되게 된다.
  • lazy 함수를 사용하므로 반드시 해당 블록 안에서 값이 초기화 되도록 한다(value)

extension 사용 전 코드

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
  	val value = intent.extras?.getLong(EXTRA_FLOWER)
    val flower = value?.let { DataSource().getFlowerForId(it) }
}

extension 사용 후 코드

private val flower by extraNotNull<Long>(EXTRA_FLOWER)
override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
  	DataSource().getFlowerForId(flower)
}
  • extension으로 정의해놓은 extraNotNull 확장 함수를 사용하여 flower를 선언한다.

reference


[TIL-240409]

0개의 댓글