코틀린 언어 용어 사전
함수extension function · 확장 프로퍼티

확장 함수

클래스를 수정하지 않고 메서드를 더하는 문법. 정적으로 해석되므로 오버라이드되지 않는다.

클래스를 수정하지 않고 마치 그 클래스의 메서드인 것처럼 함수를 더한다.

fun String.isValidEmail(): Boolean =
    matches(Regex("^[\\w.]+@[\\w.]+\\.\\w+$"))

"a@b.com".isValidEmail()      // 원래 있던 메서드처럼 쓴다

실제로는 정적 함수다

// 컴파일되면 이렇게 된다 (개념적으로)
public static boolean isValidEmail(String $this) { ... }

여기서 세 가지 성질이 따라 나온다.

  • ① private 멤버에 접근할 수 없다 — 밖에서 부르는 함수일 뿐이다
  • ② 오버라이드되지 않는다 — 정적 디스패치다
  • ③ 멤버 함수가 우선한다 — 이름이 겹치면 원래 메서드가 이긴다
open class Base
class Derived : Base()

fun Base.name() = "Base"
fun Derived.name() = "Derived"

val x: Base = Derived()
x.name()      // "Base" — 선언된 타입으로 결정된다 (런타임 타입이 아니다)

②가 함정이다 — 다형성을 기대하면 틀린다.

널 가능 타입에도 붙일 수 있다

fun String?.orEmpty(): String = this ?: ""

val s: String? = null
s.orEmpty()       // 안전 호출 없이 부를 수 있다
// 표준 라이브러리가 이 방식을 쓴다
fun CharSequence?.isNullOrBlank(): Boolean = this == null || this.isBlank()

확장 프로퍼티

val String.lastChar: Char get() = this[length - 1]

// 백킹 필드가 없다 — 상태를 저장할 수 없고 게터/세터만 정의한다

안드로이드에서의 관용구

fun View.show() { visibility = View.VISIBLE }
fun View.hide() { visibility = View.GONE }

fun Context.toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()

fun Int.dpToPx(context: Context): Int =
    (this * context.resources.displayMetrics.density).toInt()

프레임워크 클래스에 우리 팀의 관용구를 얹는 것이 가장 흔한 용도다.

남용의 경계

  • 좋은 쓰임 — 범용 유틸 · 프레임워크 클래스 보완 · 가독성 개선
  • 나쁜 쓰임 — 내가 소유한 클래스에 붙이기 (그냥 멤버로 넣으면 된다)
    • 도메인 로직을 밖으로 흩뿌리기 (응집도가 깨진다)

면접 함정

  • "확장 함수는 클래스를 수정한다" → 바이트코드상 정적 함수일 뿐이다.
  • "확장 함수를 오버라이드할 수 있다" → 정적 디스패치라 선언 타입으로 결정된다.

어디에 두나

파일 상단(최상위)에 두는 것이 기본이다

  • 임포트만 하면 어디서든 쓸 수 있다

클래스 안에 두면 그 클래스 안에서만 보인다 (멤버 확장 함수)

  • 수신자가 둘이 되어 헷갈리기 쉽다. 잘 쓰지 않는다
// 프로젝트 관례: ui/ext/ViewExt.kt · ContextExt.kt 처럼 대상별로 모은다

제네릭 확장

fun <T> List<T>.secondOrNull(): T? = if (size >= 2) this[1] else null

// 특정 타입에만 붙일 수도 있다
fun List<Int>.sumEvens() = filter { it % 2 == 0 }.sum()

표준 라이브러리가 전부 이 방식이다

// String · List · Map 의 유용한 함수 대부분이 확장 함수다
"abc".reversed()
listOf(1,2,3).sumOf { it }
mapOf(1 to "a").getOrDefault(2, "b")

그래서 "코틀린 표준 라이브러리가 풍부하다"는 인상의 상당 부분이 확장 함수 덕분이다 — 자바 클래스를 건드리지 않고도 API를 얹었다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 함수 심화 — 확장·고차·람다·inline·reified