확장 함수
기존에 정의된 클래스에 함수를 추가하여 정의할 수 있는 기능이다.
예를 들어 수정할 수 없는 서드파티 라이브러리의 클래스에 새로운 함수를 작성할 수 있게 되고 일반적인 클래스의 함수인 것처럼 일반적인 방식으로 호출할 수 있게 되는데 이러한 메커니즘을 가진 함수이다.
fun main() {
"World!".hello()
}
fun String.hello() = println("Hello $this")
// Hello World
확장 함수 형태
확장되는 유형을 참조하는 수신 타입에 .
을 붙여 새로운 함수를 정의할 수 있다.
- 수신 타입(
receiver
type
)은 확장 대상이 되는 클래스이다. this
는 수신 객체 지정 람다(람다 리시버,Lambda
Receiver
) 이다.
fun MutableList<Int>.swapIntList(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
제네릭(Generic
)을 사용하여 일반화할 수 있다.
fun <T> MutableList<T>.swapList(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
정적 바인딩
확장 함수는 정적 바인딩 되는데 함수 호출 부분에 메모리 주소값을 저장하는 작업이 컴파일 되는 시점에 동작하여 컴파일 이후의 값이 변경되지 않는 것을 의미한다.
멤버 함수가 있는 클래스에 같은 수신 타입, 같은 이름, 같은 인자의 확장 함수가 정의되면 멤버 함수가 실행된다.
fun main() {
printClassName(Rectangle())
}
fun printClassName(s : Shape) = println(s.getName()) // 확장 함수 호출
fun Shape.getName() = "Shape" // Shape 클래스의 확장 함수
fun Rectangle.getName() = "Rectangle" // Rectangle 클래스의 확장 함수
open class Shape
class Rectangle : Shape() // Rectangle 클래스가 Shape 클래스를 상속 받음
// Shape
printClassName()
에Rectagle
타입의 인스턴스를 전달하였지만 확장 함수는 정적 바인딩 되기 때문에 확장 함수 호출 부분인s.getName()
에 저장되는 메모리 주소가 이미 컴파일 되는 시점에 결정되어 있다.- 확장 함수 호출 부분인
s.getName()
부분에 이미Shape
클래스의 확장 함수인getName()
함수가 저장된 메모리 주소가 저장되어 있다. printClassName(Rectangle())
로 호출하면s.getName()
부분에 저장된 메모리 주소만을 알고 있기 때문에 이 위치에 있는 코드를 실행하게 된다.
함수의 이름이 같지만 파라미터가 달라 오버로딩(Overloading
)이 이루어지는 경우는 다른 함수를 추가하는 것이기 때문에 확장한 함수가 실행된다.
fun main() {
Test().outputTest(1)
}
fun Test.outputTest(n: Int) = println("Test $n")
class Test {
fun outputTest() = println("Test")
}
// Test 1
- 여기서 확장 함수
outputTest(n: Int)
는 멤버 함수인outputTest()
과 이름이 같지만, 파라미터가 다르기 때문에 확장한 함수가 실행된다.