확장 함수(extension functions)는 Kotlin과 Java의 주요 차별점 중 하나로, C#의 영감을 받아 언어의 첫 번째 버전에서 처음 등장했습니다. 이 기능은 상속이나 기존 클래스의 수정 없이 기존 클래스에 새로운 메소드를 추가할 수 있게 해줍니다. 이는 코드의 표현력을 크게 향상시키고 간결함을 제공합니다.
Java 및 많은 다른 언어에서는 이미 존재하는 클래스(예: String 또는 List)에 메소드를 추가할 수 없습니다. 상속이나 Decorator 패턴을 사용하지 않고는 추가가 불가능합니다. Kotlin은 확장 함수를 통한 이 단점을 해결합니다.
라이브러리의 많은 클래스(예: String, Int, List)는 직접 수정할 수 없습니다. 코드의 가독성을 높이고 중복된 로직을 피하기 위해 추가 메소드가 필요할 때가 많습니다. 그러나, 확장 함수는 실제 클래스를 수정하지 않습니다. 이들은 정적(static)으로 생성되며, 내부에서 private/protected 멤버에 접근할 수 없습니다.
Kotlin에서 확장 함수의 선언은 다음과 같이 보입니다:
fun String.lastChar(): Char = this[this.length - 1]
이제 이 메소드를 모든 String에서 호출할 수 있습니다:
"Kotlin".lastChar() // 'n'을 반환합니다.
확장 함수는 클래스의 자식에서 재정의될 수 있습니까?
아니요. 확장 함수는 클래스의 멤버가 아니므로 override를 통해 재정의할 수 없습니다: 이들의 정적 성격은 상속자에게도 나타납니다 — 컴파일러는 표현식의 유형에 따라 함수를 선택하며, 실제 객체의 유형에 따라 선택하지 않습니다.
open class Base class Derived : Base() fun Base.foo() = "base" fun Derived.foo() = "derived" fun printFoo(b: Base) { println(b.foo()) } val d = Derived() printFoo(d) // "base"를 출력합니다.
확장 함수가 객체의 상태를 변경할 수 있습니까?
확장 함수는 private/protected 상태에 접근할 수 없습니다. 그러나 mutable 타입(예: List 또는 사용자 정의 클래스)의 경우, 공용 메소드를 통해 외부 상태를 변경할 수 있습니다.
fun MutableList<Int>.addTwice(elem: Int) { add(elem) add(elem) }
확장 함수가 같은 이름의 클래스 멤버와 충돌할 수 있습니까?
확장 함수의 이름이 실제 메소드 이름과 같으면, 클래스의 멤버가 우선합니다. 멤버가 없거나 보이지 않는 경우에만 확장 함수가 호출됩니다.
class Foo { fun bar() = "member" } fun Foo.bar() = "extension" val f = Foo() println(f.bar()) // "member"
개발자가 표준 String에 프로젝트에 특화된 형식의 확장 함수를 추가하고 이를 광범위하게 사용하는 경우, 다른 개발자들은 이 메소드가 String 클래스의 표준 작업이 아님을 알아차리지 못합니다.
장점:
단점:
표준 클래스를 유틸리티 메소드로 확장하여 utils 패키지에 명확하게 로컬화하고 잘 문서화할 수 있습니다. 사용하는 부분이 명시적으로 정해진 코드 영역에 제한됩니다.
장점:
단점: