kyucumber
전체 글 보기

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

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

fun Int.factorial(): Int = (1..this).product() // 생략 operator fun Int.not() = factorial() print(10 * !6) // 7200

팩토리얼은 ! 기호를 사용해 표기하니 위와 같이 구현한다면 안된다. 함수의 이름이 not이기 때문에 논리 연산에 사용해야지 팩토리얼 연산에 연산자 오버로딩을 사용하면 안된다.

연산자 대응되는 함수
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
++a a.inc()
—a a.dec()
a + b a.plus(b)
a * b a.times(b)
a / b a.div(b)
a..b a.rangeTo(b)
a in b b.contains(a)
a += b a.plusAssign(b)

Kotlin docs operator overloading

따라서 팩토리얼을 계산하기 위해 ! 연산자를 사용하면 안된다. 이는 관례에 어긋나기 때문이다.

분명하지 않은 경우

관례를 충족하는지 아닌지 확실하지 않은 경우도 존재할 것이다. 이 경우는 infix를 활용한 확장 함수를 사용하는 것이 좋다.

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

top-level function을 사용하는 것도 좋다. 위와 같이 함수를 n번 호출하는 것은 다음과 같은 형태로 이미 구현되어 있다.

repeat(3) { print("Hello") }

규칙을 무시해도 되는 경우

지금까지 설명한 연산자 오버로딩 규칙을 무시해도 되는 중요한 경우가 있다. 이는 DSL을 설계할 때이다.

body { div { + "Some text" } }

위는 HTML DSL이며 문자열 앞에 String.unaryPlus가 사용되었지만 문제가 되지 않는다. 이는 DSL 코드이기 때문이다.

정리

연산자 오버로딩은 이름에 의미에 맞게 사용하자.

꼭 연산자 형태로 사용하고 싶다면 infix 확장 함수 또는 top-level function을 사용하자

Reference

  • 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김

개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.

오류가 있다면 댓글을 남겨주세요.