with ChatGPT 시리즈는 ChatGPT의 내용과 개인의 생각을 토대로 학습해 보는 컨텐츠입니다.
Kotlin에서는 기존 클래스의 소스 코드를 수정하지 않고도 새로운 함수나 프로퍼티를 "확장"해서 추가할 수 있는 기능을 제공합니다.
이를 통해 가독성, 재사용성, 도메인 표현력이 크게 향상됩니다.
이번 글에서는 다음 내용을 중심으로 설명드리겠습니다.
- 확장 함수란 무엇인가?
- 확장 프로퍼티란 무엇인가?
- 내부적으로 어떻게 동작하는가?
- 실무에서의 활용 예시
- 주의할 점
🔹 1. 확장 함수 (Extension Function)
확장 함수는 기존 클래스에 새로운 함수를 추가하는 것처럼 보이게 만드는 기능입니다.
→ 실제로는 정적 (static) 함수이지만, 호출 문법상 인스턴스 메서드처럼 사용할 수 있습니다.
fun String.lastChar(): Char = this[this.length - 1]
val name = "Kotlin"
println(name.lastChar()) // 출력: 'n'
- String 클래스에 lastChar()라는 메서드가 정의된 것처럼 호출할 수 있습니다.
- 실제로는 lastChar(name)와 동일하지만, 문법적으로 더 자연스럽고 가독성이 좋음
🔹 2. 확장 프로퍼티 (Extension Property)
확장 프로퍼티를 이용하여 함수뿐 아니라 프로퍼티 형태로도 확장이 가능합니다.
val Setting.firstCar: Char
get() = this[0]
val word = "Extension"
println(word.firstChar) // 출력: 'E'
- 마치 String에 firstChar라는 기본 속성이 있는 것처럼 사용
- 단점: 확장 프로퍼티는 상태를 가질 수 없음 (즉, backing field 없음)
🔹 3. 내부적으로는 어떻게 동작할까?
확장 함수는 정적 디스패치 (static dispatch)로 동작합니다.
즉, 변수의 선언된 타입에 따라 확장 함수가 정해지며, 실제 런타임 타입은 영향을 주지 않습니다.
open class Parent
class Child : Parent()
fun Parent.greet() = "Hello from Parent"
fun Child.greet() = "Hello from Child"
val obj: Parent = Child()
println(obj.greet()) // 출력: "Hello from Parent"
- 상속 구조가 있어도 override는 불가능
- 이는 확장 함수가 멤버 함수처럼 보이지만 사실은 전혀 아님을 보여주는 대표적 예
🔹 4. 실무에서의 활용 예시
✅ 도메인 객체에 도메인 용어로 기능 추가
data class Money(val amount: Int)
fun Money.isPositive(): Boolean = this.amount > 0
- 서비스 코드에서 if (price.isPositive())처럼 읽기 좋은 표현이 가능
✅ 유틸리티 함수 대체
기존 Java 스타일:
StringUtil.isBlank(str);
Kotlin 스타일:
fun String.isBlankOrNull(): Boolean = this.isBlank() || this.isEmpty()
str.isBlankOrNull()
- 명시적으로 어떤 객체를 기준으로 동작하는지 표현 가능
✅ 테스트 헬퍼
fun Result.shouldBeSuccess() {
assertTrue(this.isSuccess)
}
- 테스트 DSL 형태로 사용 가능
🔹 5. 주의할 점
주의점 | 설명 |
오버라이드 불가 | 확장 함수는 런타임 기준이 아니라 정의된 타입 기준으로 호출됨 |
충돌 방지 | 동일한 확장 함수가 다른 곳에서 중복되면 ambiguous error 발생 |
상태 없음 | 확장 프로퍼티는 backing field를 가질 수 없음 (즉, 값을 저장 불가) |
멤버 함수보다 우선순위 낮음 | 클래스 내부에 동일한 이름의 멤버 함수가 있다면, 확장 함수는 무시됨 |
🚀 결론
구분 | 확장 함수 | 확장 프로퍼티 |
용도 | 기존 타입에 함수 추가 | 기존 타입에 속성 추가처럼 보이게 |
사용 위치 | 유틸, DSL, 테스트 등 | 읽기 중심의 프로퍼티 표현 |
특징 | 정적 디스패치, 오버라이드 불가 | 값 저장 불가 (getter만 가능) |
장점 | 가독성, 재사용성 향상 | 표현력 있는 코드 작성 가능 |
확장 기능은 Kotlin이 추구하는 "기존 타입을 변경하지 않고 기능을 덧붙인다"는 실용주의 철학을 잘 보여줍니다.
잘 활용하면 라이브러리처럼 자연스러운 도메인 언어로 코드를 작성할 수 있습니다.
🤔 추가로 생각해 볼 질문들
- 확장 함수와 멤버 함수가 충돌할 때 어떤 것이 우선될까?
- 확장 함수는 internal, private 접근 제한이 가능할까?
- 확장 함수 안에서 this는 실제 어떤 타입을 참조할까?
- 확장 함수로 "DSL 스타일 코드"를 작성하려면 어떤 방식이 좋을까?
- 확장 함수도 generic 하게 만들 수 있을까?