안드로이드 개발자 노트
[이펙티브 코틀린] Item44. 멤버 확장 함수의 사용을 피하라 본문
반응형
확장 함수는 첫 번째 아규먼트로 리시버를 받는 단순한 일반 함수로 컴파일 됩니다.
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를 업데이트 한다)
}
마지막으로, 경험이 적은 개발자의 경우 확장 함수를 보면, 직관적이지 않을 수 있습니다.
반응형
'Kotlin > 이펙티브 코틀린' 카테고리의 다른 글
[이펙티브 코틀린] Item45. 불필요한 객체 생성을 피하라 (0) | 2024.01.14 |
---|---|
[이펙티브 코틀린] Item43. API의 필수적이지 않는 부분을 확장 함수로 추출하라 (0) | 2024.01.14 |
[이펙티브 코틀린] Item42. compareTo의 규약을 지켜라 (0) | 2024.01.13 |