연산자 오버로딩은 굉장히 강력한 기능이지만 위험할 수 있다.
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
- 이펙티브 코틀린 - 프로그래밍 인사이트, 마르친 모스칼라 지음, 윤인성 옮김
개인적인 기록을 위해 작성된 글이라 잘못된 내용이 있을 수 있습니다.
오류가 있다면 댓글을 남겨주세요.