확장 함수 (Extension Function)

헨도·2026년 3월 10일

Kotlin

목록 보기
2/4
post-thumbnail

확장(Extension) 이란?

확장은 상속이나 데코레이터 패턴 없이 기존 클래스나 인터페이스에 새로운 기능을 추가하는 Kotlin 기능이다.

즉, 실제로 클래스를 수정하지 않고도 마치 클래스 내부에 정의된 멤버 함수처럼 사용할 수 있는 함수를 만들 수 있다.

예를 들어 String 클래스에 새로운 기능을 추가할 수 있다.

fun String.lastChar(): Char {
	return this[this.length - 1]
}

fun main() {
	println("Kotlin".lastChar())
}
n

이 함수는 String 클래스에 정의되어 있지 않지만 마치 멤버 함수처럼 호출할 수 있다.


확장 함수 (Extension Functions)

정의 방법

확장 함수는 다음과 같은 형태로 정의한다.

fun 수신자타입.함수명(...) { ... }

Ex.

fun String.lastChar(): Char {
	return this[this.length - 1]
}

여기서 String수신자 타입(receiver type)이라고 한다.

this의 의미

확장 함수 내부에서 this확장을 호출한 객체(수신자 객체)를 가리킨다.

fun String.lastChar(): Char {
	return this[this.length - 1]
}

위 코드에서 this"Kotlin" 같은 실제 문자열 객체를 의미한다.


제네릭 지원

확장 함수는 제네릭 타입도 지원한다.

fun <T> List<T>.second(): T {
	return this[1]
}

fun main() {
	val numbers = listOf(1, 2, 3)
    println(numbers.second())
}
2

왜 확장 함수를 사용하는가?

확장 함수는 다음과 같은 상황에서 유용하다.

1. 외부 라이브러리 기능 확장

라이브러리 코드를 수정할 수 없을 때 기능을 추가할 수 있다.

fun String.isEmail(): Boolean {
	return contains("@")
}

2. 유틸리티 코드 정리

기존 Java에서는 보통 다음과 같은 util 클래스를 만들었다.

StringUtils.isBlank(str)

하지만 Kotlin에서는

str.isBlank()

처럼 객체 중심 API 스타일로 사용할 수 있다.

3. 가독성 개선

확장 함수를 사용하면 도메인 중심 코드를 만들 수 있다.

fun LocalDate.isWeekend(): Boolean {
	return this.dayOfWeek == DayOfWeek.SATURDAY || this.dayOfWeek == DayOfWeek.SUNDAY
}

사용

if (date.isWeekend()) { ... }

주요 특징 및 주의사항

정적 결정 (Static Dispatch)

확장 함수는 런타임이 아닌 컴파일 시점에 호출이 결정된다.

즉, 변수의 실제 타입이 아니라 선언된 타입에 따라 결정된다.

open class Shape
class Rectangle: Shape()

fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"

fun printClassName(s: Shape) {
	println(s.getName())
}

fun main() {
	printClassName(Rectangle())
}
Shape

이유는 s의 타입이 Shape로 선언되어 있기 때문이다.

즉, 확장 함수는 다형성(polymorphism)을 지원하지 않는다.


멤버 함수 우선

클래스 내부에 동일한 이름의 멤버 함수가 존재한다면 항상 멤버 함수가 우선적으로 호출된다.

class Example {
	fun printFunctionType() {
    	println("Class method")
    }
}

fun Example.printFunctionType() {
	println("Extension function")
}

fun main() {
	Example().printFunctionType()
}
Class method

즉, 확장 함수는 기존 멤버 함수를 override할 수 없다.


확장 함수는 실제로 클래스에 추가되지 않는다.

확장 함수는 컴파일 시 static 함수로 변환된다.

Ex.

fun String.lastChar(): Char

는 실제로 다음과 유사한 코드로 컴파일된다.

public static char lastChar(String receiver)

즉, 확장 함수는...

  • 클래스 구조를 변경하지 않으며
  • 실제 멤버 함수도 아니다.

Nullable 수신자

확장 함수는 Nullable 타입에도 정의할 수 있다.

fun Any?.toStringSafe(): String {
	if (this == null) return "데이터가 없습니다."
    return this.toString
}

fun main() {
	val name: String? = null
    println(name.toStringSafe())
}
데이터가 없습니다.

이처럼 null-safe 로직을 공통화할 때 유용하다.


확장 프로퍼티 (Extension Properties)

확장 프로퍼티도 정의할 수 있다.

val <T> List<T>.lastIndex: Int
	get() = size - 1

사용

println(listOf(1, 2, 3).lastIndex)

Backing Field 제한

확장 프로퍼티는 실제 필드(Backing Field)를 가질 수 없다.

다음과 같은 것은 불가능하다

val String.count = 0

이유는 확장이 기존 클래스 외부에서 정의되기 때문이다.

그래서 확장 프로퍼티는 getter / setter 만 정의할 수 있다.


기타 확장 기능

Companion Object 확장

동반 객체에도 확장을 정의할 수 있다.

class MyClass {
	companion object
}

fun MyClass.Companion.printHello() {
	println("Hello from Companion Extension!")
}

fun main() {
	MyClass.printHello()
}

확장 함수 정리

특징설명
기존 클래스 수정 불필요외부 클래스에도 기능 추가 가능
실제 멤버 함수 아님컴파일 시 static 함수로 변환
정적 디스패치변수 타입 기준으로 결정
멤버 함수 우선override 불가능
nullable 확장 가능null-safe 로직 구현 가능

마무리

Kotlin의 확장 함수는 기존 클래스 구조를 변경하지 않고 기능을 확장할 수 있는 기능이다.

특히

  • 유틸 함수 정리
  • 도메인 중심 코드 작성
  • 라이브러리 기능 확장

같은 상황에서 매우 유용하게 사용할 수 있다.

다만 정적 디스패치(static dispatch)라는 특징 때문에 다형성을 기대하고 사용하면 다른 결과가 나올 수 있으므로 주의해야 한다.

profile
Junior Backend Developer

0개의 댓글