본문 바로가기

카테고리 없음

연산자 오버로드를 할 때는 의미에 맞게 사용하기

연산자 오버로딩은 굉장히 좋은 기능이지만 굉장히 위험할 수도 있습니다 예를 들어 팩토리얼 구하는 함수를 생각해 봅시다

fun Int.factorial(): Int = (1..this).product()

fun Iterble<Int>.product(): Int =
    fold(1) {acc, i -> acc *1 }

이 한수는 Int 확장 함수로 정의되어 있으므로, 굉장히 편리하게 사용할 수 있습니다.

코틀린은 10*6! 코틀린은 이런 연산자를 지원하지 않지만, 다음과 같이 연산자 오버로딩을 활용하면, 만들어 낼 수 있습니다.

operator fun Int.not() = factorial()
print(10 * !6) 

 

이렇게 사용할수 있지만 이렇게 사용해서는 안됩니다.  이렇게 사용하면 오해의 소지가 있습니다. 코틀린의 각 연산자의 의미는 항상 같게 유지 됩니다 스칼라와 같은 일부 프로그래밍  언어는 무제한 연산자 오버로딩을 지원합니다, 이정도의 자유는 많은 개발자가 해당 기능을 오용하게 만듭니다. 예를들어 +의 연산자ㅏ 일반적인 의미로 사용되지 않고 있다면  연산자를 볼 때마다 연산자를 개별적으로 이해해야 하기 때문에 코드를 이해하기 어려울 것 입니다. 코틀린은 각각 연산자에 구체적인 의미가 부여 되어있어 이러한 문제가 없습니다. 예를들어

x+y == z  ->    x.plus(y).equal(z) 같은 코드로 변환 됩니다.

참고로 플로스의 리턴 타입이 nullable이라면 다음과 같이 변환 됩니다

(x.plus(y))?.equal(z) ? : (z == null) 

이는 구체적인 이름을 가진 함수이며 모든 연산자가 이러한 이름이 나타내는 역할을 할 거라고 기대됩니다. 이처럼 이름만으로 연산자의 사용이 크게 제한 됩니다, 따라서 팩토리얼을 계산하기 위해서 ! 연산자를 사용하면 안 됩니다. 이는 관례에 어긋나기 때문입니다.

 

분명하지 않은 경우

 

하지만 관례를 충족하는지 아닌지 확실하지 않을 때가 있습니다. 예를들어 함수를 세 배 한다는 것은 무슨 의미 일까요?

어떤 사람은 세번 반복하는 함수를 만들어 생각 할 수 있습니다

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")}

의미가 명확하지 않다면, inflx를 활용한 확장 함수를 사용하는 것이 좋습니다. 일반적인 이항 연산자 형태처럼 사용할 수 있습니다.

 

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

val tripledHello = 3 timesRepeated {print("Hello")}
tripledHello()

 

톰레벨 함수를 사용하는 것도 좋습니다. 사실 함수를 n번 호출하는 것은 다음과 같은 형태로 이미 stdlibdp에 그현되어 있습니다

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

 

 

규칙을 무시해도 되는 경우

지금까지 설명한 연산자 오버로딩 규칙을 무시해도 되는 중요한 경우가 있습니다. 바로 도메인 특화 언어를 설계할 때입니다. 고전적인 HTML DSL을 생가해 봅시다

body{
    div{
        +"Some text"
    }
}

문자열 압에 String.unaryPlus가 사용된 것을 볼 수 있습니다. 이렇게 코드를 작성해도 되는 이유는 이 코드가 DSL 코드이기 때문입니다.

 

 

정리

오버로딩은 그 이름의 의미에 맞게 사용해 주세요. 연산자 의미가 명확하지 않다면 연산자 오버로딩을 사용되지 않는 것이 좋습니다. 대신 이름이 있는 일반 함수를 사용하기 바랍니다. 꼬 연산자 같은 형태로 사용하고 싶다면 inflex 확장 함수 또는 톱레벨 함수를 활용하세요

728x90