=================
== The Archive ==
=================

Kotlin 멤버 함수와 확장 함수의 동작 방식 차이 및 내부 원리

|

AI Summary

  • 코틀린 멤버 함수는 클래스 내부에 정의되어 캡슐화된 상태에 접근 가능하며 동적 바인딩으로 런타임 객체 타입에 따라 호출된다.
  • 확장 함수는 클래스 외부에 정의되어 public 멤버만 접근 가능하고, 컴파일 시점에 선언된 변수 타입 기준으로 정적 바인딩된다.
  • 확장 함수는 내부적으로 수신 객체를 첫 번째 인자로 받는 정적 메서드로 자바 바이트코드에 컴파일되어 런타임 타입과 무관하게 호출된다.
  • 멤버 함수와 동일한 시그니처를 가진 확장 함수는 멤버 함수가 우선 호출되며, 이를 섀도잉(Shadowing)이라 한다.
  • 확장 함수는 널 가능 타입에도 정의할 수 있어 null 참조 시에도 NPE 없이 안전하게 동작할 수 있다.
Updated: 2026-03-06 11:47 UTC

Introduction


1. 멤버 함수와 확장 함수의 개념적 차이


2. 동적 바인딩(Dynamic Binding) vs 정적 바인딩(Static Binding)

예제 코드

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
open class Animal {
    open fun makeSound() = println("멤버 함수: 동물 소리")
}

class Dog : Animal() {
    override fun makeSound() = println("멤버 함수: 멍멍!")
}

// Animal 클래스의 확장 함수
fun Animal.eat() = println("확장 함수: 동물이 냠냠 먹습니다.")

// Dog 클래스의 확장 함수
fun Dog.eat() = println("확장 함수: 강아지가 뼈다귀를 먹습니다.")

실행 및 결과

1
2
3
4
5
6
fun main() {
    val myPet: Animal = Dog() // 참조 타입은 Animal, 실제 인스턴스는 Dog

    myPet.makeSound() // 1. 멤버 함수 호출
    myPet.eat()       // 2. 확장 함수 호출
}

실행 결과:

1
2
멤버 함수: 멍멍!
확장 함수: 동물이 냠냠 먹습니다.

원인 분석


3. 내부 동작 원리 (Decompiled Java Code)

1
2
3
4
// 내부적으로 변환된 자바 코드의 형태
public static void eat(Animal receiver) {
    System.out.println("확장 함수: 동물이 냠냠 먹습니다.");
}

4. 이름 충돌과 섀도잉 (Shadowing)

1
2
3
4
5
6
class Animal {
    fun sleep() = println("멤버 함수: 동물이 잡니다.")
}

// 멤버 함수와 동일한 시그니처를 가진 확장 함수
fun Animal.sleep() = println("확장 함수: 동물이 쿨쿨 잡니다.")

5. Null 처리에 강한 확장 함수

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fun Animal?.yawn() {
    if (this == null) {
        println("수신 객체가 Null입니다.")
    } else {
        println("동물이 하품합니다.")
    }
}

val myPet: Animal? = null
myPet.yawn() // NullPointerException이 발생하지 않고 정상 실행됨

요약


Kotlin Playground

Categories:

Tags: