Language/Kotlin

[Kotlin] Extension Functions (확장 함수)

Junhong Kim 2023. 6. 4. 16:03
728x90
반응형

확장 함수란?

확장 함수는 Kotlin 기능 중 하나로, 기존 클래스에 새로운 함수를 추가하는 것 입니다. 즉, 이미 존재하는 클래스에 대해 확장 함수를 정의하여 해당 클래스의 인스턴스에서 멤버 함수처럼 호출할 수 있습니다. 확장 함수는 기존 클래스의 내부에 접근하지 않고도 외부에서 확장 함수를 작성하여 사용할 수 있습니다. 이를 통해 기존 클래스의 기능을 확장하거나, 특정 작업을 수행하는 유틸리티 함수를 제공할 수 있습니다.

 

확장 함수는 점 표기법을 사용하여 호출되며, 호출 대상 객체는 `수신 객체(receiver object)`라고 불립니다. 확장 함수를 선언할 때는 해당 확장 함수가 적용될 클래스를 수신 객체 타입으로 지정하고 그 뒤에 확장 함수 이름을 지정합니다. 그럼 이제 확장 함수를 선언하고 사용하는 방법에 대해 알아보겠습니다.

기본 문법

확장 함수 선언 방법

다음은 확장 함수의 선언 구문입니다.

fun 수신객체타입.확장함수명(): 반환값 {
	// todo
}
  • fun
    • 함수를 선언합니다.
  • 수신 객체 타입
    • 확장 함수를 적용할 수신 객체 타입, 해당 클래스의 인스턴스로 확장 함수를 호출할 수 있습니다.
  • 확장 함수
    • 확장 함수의 이름, 일반적인 함수 선언 방법과 동일한 규칙을 따릅니다.
  • 반환 값
    • 확장 함수가 반환하는 결과 값

수신 객체 지정 람다와 함께 사용하기

수신 객체 지정 람다를 사용하면 확장 함수를 사용하는 동안 특정 객체를 더 쉽게 접근할 수 있습니다. 수신 객체 지정 람다는 함수 내에서 `this`를 사용해 수신 객체에 접근 할 수 있습니다. 예를 들어 수신 객체 지정 람다와 함께 사용하는 예시는 다음과 같습니다. 이 확장 함수는 List의 각 요소를 특정 조건에 따라 필터링한 결과를 반환합니다.

fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
    val result = mutableListOf<T>()
    for (item in this) {
        if (predicate(item)) {
            result.add(item)
        }
    }
    return result
}

fun main() {
    val list = listOf(1, 2, 3, 4)
    val filteredList = list.customFilter { it % 2 == 0 }
    println(filteredList)
}

--

[2, 4]

위 코드에서 `customFilter()` 함수를 호출할 때 수신 객체 지정 람다를 사용하여 필터링 조건을 지정합니다. 람다 내에서 `it` 키워드를 통해 현재 element에 접근할 수 있으며, 필터링 조건을 작성합니다. 이렇게 수정된 코드를 실행하면, `filteredList` 변수에는 주어진 List에서 짝수인 요소만 필터링된 새로운 List를 반환됩니다.

확장 함수 활용

기본 타입 확장 함수

기본 타입(Int, Double Boolean 등)에 대해 확장 함수를 작성할 수 있습니다. 확장 함수를 활용하여 기본 타입에 새로운 기능을 추가할 수있습니다. 예를 들어 숫자를 제곱하는 `square()` 확장 함수는 다음과 같습니다.

fun Int.square(): Int {
    return this * this
}

fun main() {
    val number = 2
    val squaredNumber = number.square()
    println(squaredNumber)
}

---

4

 

문자열 확장 함수

문자열에 대해 확장 함수를 작성할 수 있습니다. 기본 타입과 마찬가지로 확장 함수를 활용하여 문자열 타입에 새로운 기능을 추가할 수 있습니다. 예를 들어 주어진 문자열을 무작위로 섞는 `shuffle()` 확장 함수는 다음과 같습니다.

fun String.shuffle(): String {
    val charArray = this.toCharArray()
    charArray.shuffle()
    return String(charArray)
}

fun main() {
    val originalString = "HelloKotlin"
    val shuffledString = originalString.shuffle()
    println(shuffledString)
}

--

tlionlelHoK

 

컬렉션 확장 함수

컬렉션(List, Set, Map 등)에 대해 확장 함수를 작성할 수 있습니다. 확장 함수를 활용하여 컬렉션의 element를 처리하거나 변환하는 작업을 만들 수 있습니다. 예를 들어 List의 각 element를 제곱한 결과의 List를 반환하는 `square()` 확장 함수는 다음과 같습니다.

fun List<Int>.square(): List<Int> {
    return this.map {
        it * it
    }
}

fun main() {
    val list = listOf(1, 2, 3, 4)
    println(list.square())
}

---

[1, 4, 9, 16]

파일 확장 함수

파일 다루기와 관련된 작업에도 확장 함수를 활용할 수 있습니다. 파일 확장 함수를 작성하여 파일을 보다 편리하게 조작하고 관리할 수 있습니다. 예를 들어 파일의 확장자를 반환하는 `getExtension()` 확장 함수는 다음과 같습니다.

fun File.getExtension(): String {
    val fileName = this.name
    val dotIndex = fileName.lastIndexOf(".")
    return if (dotIndex > 0) fileName.substring(dotIndex + 1) else ""
}

fun main() {
    val file = File("example.txt")
    val extension = file.getExtension()
    println(extension)
}

---

txt

확장 함수의 제약 사항

가시성과 접근 범위

확장 함수는 클래스의 멤버 함수처럼 호출할 수 있지만, 클래스 내부에서만 접근 가능한 private 멤버에는 접근할 수 없습니다. 예를 들어 다음과 같은 확장 함수에서 private 멤버인 `privateProperty`에 접근할 수 없습니다.

class MyClass {
    private val privateProperty = "Private"

    fun doSomething() {
        println("Doing something")
    }
}

fun MyClass.myExtensionFunction() {
    println(privateProperty) // 컴파일 에러
    doSomething()
}

 

확장 함수 내부에서는 확장 대상 클래스의 `public 멤버(doSomething)`에는 접근할 수 있지만 `private 멤버(privateProperty)`에는 접근할 수 없습니다.

이름 충돌

확장 함수를 정의할 때, 클래스의 멤버 함수와 동일한 이름으로 같은 시그니처를 가지는 함수가 있는 경우 이름 충돌이 발생합니다. 예를들어 이름 충돌이 발생하는 확장 함수는 다음과 같습니다.

class MyClass {
    fun printInfo() {
        println("Class method")
    }
}

fun MyClass.printInfo() {
    println("Extension method")
}

fun main() {
    val myObject = MyClass()
    myObject.printInfo() // `Class method` 출력
}

위 예시에서 MyClass에 `printInfo()`라는 멤버 함수와 확장 함수가 모두 존재합니다. 이 경우, 인스턴스의 `printInfo()`를 호출하면 클래스의 멤버 함수가 우선적으로 호출되어 `Class method`가 출력됩니다. 따라서, 확장 함수를 작성할 때는 클래스의 기존 멤버 함수와 이름 충돌이 발생하지 않도록 주의해야 합니다.

확장 함수는 언제 사용해야 할까?

기존 클래스에 새로운 기능을 추가하고 싶을 때

확장 함수는 기존 클래스의 코드를 수정하지 않고 새로운 기능을 추가할 수 있는 방법을 제공합니다. 따라서, 기존 클래스의 변경 없이 새로운 기능을 추가하고 싶을 때 확장 함수를 사용하여 기능을 확장합니다.

외부 라이브러리의 클래스에 기능을 추가하고 싶을 때

사용자 정의 클래스가 아닌 외부 라이브러리의 클래스처럼 직접 수정할 수 없는 클래스에도 확장 함수를 사용할 수있습니다. 이를 통해 외부 라이브러리의 클래스에 새로운 기능을 추가하거나 기존 기능을 확장할 수 있습니다.

코드의 가독성을 향상 시키고 싶을 때

확장 함수는 코드를 더 읽기 쉽고 자연스럽게 만들어 줍니다. 특히, DSL(Domain-Specific Language) 구축이나 특정 작업을 수행하는 함수를 작성할 때 확정 함수를 사용하면 가독성을 향상 시킬 수 있습니다. (참고: Kotlin DSL 간단히 알아보기)

일급 컬렉션이 필요할 때

자바에서는 특정 타입의 컬렉션에 추가 기능을 부여하기 위해 별도의 클래스를 생성하여 일급 컬렉션을 만드는 방법을 사용합니다. 코틀린에서는 일급 컬렉션 대신 확장 함수를 정의하는 컬렉션에 새로운 기능을 추가하는 방법을 고려해볼 수 있습니다.

참고

https://kotlinlang.org/docs/extensions.html

728x90
반응형