반응형
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
관리 메뉴

안드로이드 개발자 노트

[이펙티브 코틀린] Item12. 연산자 오버로드를 할 때는 의미에 맞게 사용하라 본문

Kotlin/이펙티브 코틀린

[이펙티브 코틀린] Item12. 연산자 오버로드를 할 때는 의미에 맞게 사용하라

어리둥절범고래 2023. 9. 24. 21:12
반응형

연산자 오버로딩은 굉장히 강력한 기능이지만, 위험할 수 있습니다.

예를 들어 팩토리얼을 구하는 함수를 생각해 봅시다.

 

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이므로 논리연산에 맞게 사용해야지, 팩토리얼 연산에 사용하면 안됩니다.

관례에 어긋나기 때문입니다.
이는 구체적인 이름을 가진 함수이며, 모든 연산자가 이러한 이름이 나타내는 역할을 할 거라고 기대됩니다.

 


분명하지 않은 경우

 

관례를 충족하는지 아닌지 확실하지 않을 때가 문제입니다.

예를들어 함수를 곱한다는 것 (* 연산자)의 의미를 두가지로 해석할 수 있습니다.

  • 새로운 함수(() → Unit)를 만들어 낸다.
  • 같은 함수를 n번 호출한다.

 

의미가 명확하지 않다면, infix를 활용한 확장 함수를 사용하는 것이 좋습니다.

일반적인 이항 연산자 형태처럼 사용할 수 있습니다.

infix fun Int.timesRepeated(operation: ()->Unit) = {
    repeat(this) { operation() }
}

val tripledHello = 3 timesRepeated { print("Hello") } // 이항 연산자 처럼 사용
tripleHello() // 출력 : HelloHelloHello

 

톱레벨 함수(top-level function)을 사용하는 것도 좋습니다.

repeat(3) { print("Hello") } // 출력: HelloHelloHello

 

 


규칙을 무시해도 되는 경우

 

지금까지 설명한 연산자 오버로딩 규칙을 무시해도 되는 경우가 있습니다.

바로 도메인 특화 언어(Domain Specific Language, DSL)를 설계할 때 이며, 해당 도메인을 위해 설계된 DSL 이기 때문에 이러한 규칙을 무시할 수 있습니다.

// HTML DSL. 코틀린 문법을 활용해서 만든 DSL
body {
    div {
        +"Some Text"
    }
}

 

문자열 앞에 String.unaryPlus가 사용된 것을 볼 수 있습니다.

 


정리

 

  • 연산자 오버로딩은 그 이름의 의미에 맞게 사용해야 한다.
  • 연산자 의미가 명확하지 않다면, 연산자 오버로딩을 사용하지 않는 것이 좋다. 대신 일반 함수를 사용하길 바란다.
  • 꼭 연산자 같은 형태로 사용하고 싶다면 Infix 확장 함수 또는 톱레벨 함수를 활용하는 것이 좋다.
반응형