코틀린 스터디/이펙티브 코틀린

아이템12) 연산자 오버로드를 할 때는 의미에 맞게 사용하라

막이86 2024. 3. 8. 17:56
728x90

이펙티브 코루틴을 요약한 내용입니다

  • 연산자 오버로딩은 강력한 기능이지만 ‘큰 힘에는 큰 책임이 따른다’라는 말처럼 위험할 수 있음
  • 연산자 오버로딩의 매력
fun Int.factorial(): Int = (1..this).product()

fun Iterable<Int>.product(): Int = fold(1) { acc, i -> acc * i }
  • Int 확장 함수로 정의되어 있으므로, 편리하게 사용할 수 있음
print(10 * 6.factorial()) // 7200
  • 연산자 오버로딩을 활용하면 !기호로 팩토리얼을 표현할수 있음
    • 이렇게 사용하면 안됨
    operator fun Int.not() = factorial()
    print(10 * !6) // 7200
    
  • 함수의 이름이 not이므로 논리 연산에 사용해야지, 팩토리얼 연산에 사용하면 안됨
  • 모든 연산자는 연산자 대신 함수로도 호출할 수 있음
  • print(10 * 6.not()) // 7200
  • 코틀린의 모든 연산자는 표와 같은 구체적인 이름을 가진 함수에 대한 별칭

  • 각 연산자의 의미는 항상 같게 유지 됨
    • 매우 중요한 설계 결정

분명하지 않은 경우

  • 예를 들어 함수를 세 배 한다는 것은 무슨 의미?
    • 함수를 세번 반복하는 새로운 함수를 생각 할 수 있음
    operator fun Int.times(operation: () -> Unit): () -> Unit = { repeat(this) { operation() } }
    val tripledHello = 3 * { print("Hello") }
    tripledHello()
    
    • 어떤 코드가 함수를 세 번 호출하는 것을 생각 할 수 있음
    operator fun Int.times(operation: () -> Unit) {
    	repeat(this) { operation() }
    }
    
    3 * { print("Hello") }
    
  • 의미가 명확하지 않다면, infix를 활용한 확장 함수를 사용하는 것이 좋음
infix fun Int.timesRepeated(operation: ()-> Unit) = {
	repeat(this) { operation() }
}

val tripledHell = 3 timesRepeated { print("Hello") }
tripedHello()
  • 톱레벨 함수(top-level function)를 사용하는 것도 좋음
  • 함수를 n번 호출하는 것은 이미 stdlib에 구현되어 있음
repeat(3) { print("Hello") }

규칙을 무시해도 되는 경우

  • 연산자 오버로딩 규칙을 무시해도 되는 중요한 경우가 있음
    • 도메인 특화 언어(DSL)를 설계할 때

정리

  • 연산자 오버로딩은 그 이름의 의미에 맞게 사용
  • 연산자 의미가 명확하지 않다면, 연산자 오버로딩을 사용하지 않는 것이 좋음
  • 이름이 있는 일반 함수를 사용 하는 것이 좋음
  • 꼭 연산자 같은 형태로 사용하고 싶다면, infix 확장 함수 또는 톱레벨 함수를 활용
728x90