반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

안드로이드 개발자 노트

[이펙티브 코틀린] Item44. 멤버 확장 함수의 사용을 피하라 본문

Kotlin/이펙티브 코틀린

[이펙티브 코틀린] Item44. 멤버 확장 함수의 사용을 피하라

어리둥절범고래 2024. 1. 14. 15:08
반응형

확장 함수는 첫 번째 아규먼트로 리시버를 받는 단순한 일반 함수로 컴파일 됩니다.

fun String.isPhoneNumber(): Boolean = 
    length == 7 && all { it.isDigit() } // 컴파일 전

fun String.isPhoneNumber(): Boolean = 
    '$this'.length == 7 && '$this'.all { it.isDigit() } // 컴파일 후

확장 함수를 클래스 멤버로 정의할 수도 있고, 인터페이스 내부에 정의할 수도 있습니다.

하지만 이는 사용하지 않는 것이 좋습니다.

 

첫 번째 이유로는, 가시성을 제한할 수 있다고 생각할 수 있지만 확장만으로 가시성을 제한하지 못합니다.

이는 단순하게 확장 함수를 사용하는 형태를 어렵게 만들 뿐입니다.

// 이렇게 하지 마세요.
class PhoneBookCorrect {
    fun String.isPhoneNumber() = 
        length == 7 && all { it.isDigit() }
}

PhoneBookCorrect().apply { "1234567890".isPhoneNumber() }

확장 함수의 가시성을 제한하고 싶다면, 멤버로 만들지 말고, 가시성 한정자를 붙여 주면 됩니다.

class PhoneBookCorrect {
    //...
}

private fun String.isPhoneNumber() = 
    length == 7 && all { it.isDigit() }

 

두 번째 이유로, 레퍼런스를 지원하지 않습니다.

val ref = String::isPhoneNumber
val str = "1234567890"
val boundedRef = str::isPhoneNumber

val refX = PhoneBookCorrect::isPhoneNumber // 컴파일 에러
val book = PhoneBookCorrect() 
val bookRefX = book::isPhoneNumber // 컴파일 에러

 

세 번째 이유로, 암묵적 접근을 할 때 두 리시버 중에 어떤 리시버가 선택될지 혼동됩니다.

class A {
    val a = 10
}

class B {
    val a = 20
    val b = 30
    
    fun A.test() = a + b // 40일까? 50일까?
}

fun main() {    
    B().apply { println(A().test()) } // 정답 : 40
}

 

네 번째로, 확장 함수가 외부에 있는 다른 클래스를 리시버로 받았을 때, 해당 함수가 어떤 동작을 하는지 명확하지 않습니다.

class A {
    var state = 10
}

class B {
    var state = 20
    
    fun A.update() = state + 10  // A와 B중에서 어떤 것을 업데이트 할까요?
}

fun main() { 
    B().apply { println(A().update()) } // 정답 : 20 (A를 업데이트 한다)
}

 

마지막으로, 경험이 적은 개발자의 경우 확장 함수를 보면, 직관적이지 않을 수 있습니다.

반응형