본문 바로가기
Kotlin

[Kotlin in Action] 7.1 산술 연산자 오버로딩

by Nhahan 2025. 3. 26.

코틀린에서 관례를 사용하는 가장 단순한 예는 산술 연산자다. 자바에서는 원시 타입에 대해서만 산술 연산자를 사용할 수 있고, 추가로 String에 대해 + 연산자를 사용할 수 있다.

그러나 다른 클래스에서도 산술 연산자가 유용한 경우가 있다. 예를 들어, BigInterger 클래스를 다룬다면 add 메소드를 명시적으로 호출하기보다는 + 연산을 사용하는 편이 더 낫다. 컬렉션에 원소를 추가하는 경우에도 += 연산자를 사용할 수 있으면 더 좋다. 코틀린에서는 그런 일이 가능하다.

 

7.1.1 이항 산술 연산 오버로딩

data class Point(val x: Int, val y: Int)

Point에서 지원하고픈 첫 번째 연산은 두 점을 더하는 연산이다. 이 연산은 두 점의 X 좌표와 Y 좌표를 각각 더한다. 다음 코드는 + 연산자 구현을 보여준다.

// plus 연산자 구현하기
data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point { // "plus"라는 이름의 연산자 함수를 정의한다.
        return Point(x + other.x, y + other.y) // 좌표를 성분별로 더한 새로운 점을 반환한다.
    }
}

>>> val p1 = Point(10, 20)
>>> val p2 = Point(30, 40)
>>> println(p1 + p2) // +로 계산하면 "plus" 함수가 호출된다.
Point(x=40, y=60)

plus 함수 앞에 operator 키워드를 붙여야 한다. 연산자를 오버로딩하는 함수 앞에는 꼭 operator가 있어야한다.

// 연산자를 확장 함수로 정의하기
operator fun Point.plus(other: Point): Point {
    return Point(x + other.x, y + other.y)
}

이 구현도 앞의 구현과 똑같다.

 

코틀린에서는 프로그래머가 직접 연산자를 만들어 사용할 수 없고, 언어에서 미리 정해둔 연산자만 오버로딩할 수 있으며, 관례에 따르기 위해 클래스에서 정의해야 하는 이름이 연산자별로 정해져있다.

함수 이름
a * b time
a / b div
a % b mod(1.1부터 rem)
a + b plus
a - b minus

직접 정의한 함수를 통해 구현하더라도 연산자 우선순위는 언제나 표준 숫자 타입에 대한 연산자 우선순위와 같다. 예를 들어, a + b * c라는 식에서는 언제나 곱셈이 항상 덧셈보다 먼저 수행된다.

 

7.1.2 복합 대입 연산자 오버로딩

plus와 같은 연산자를 오버로딩하면 코틀린은 + 연산자뿐 아니라 그와 관련 있는 연산자인 +=도 자동으로 함께 지원한다. +=, -= 등의 연산자는 복합 대입(compound assignment) 연산자라 불린다.

>>> var point = Point(1, 2)
>>> point += Point(3, 4)
>>> println(point)
Point(x=4, y=6)

 

경우에 따라 += 연산이 객체에 대한 참조를 다른 참조로 바꾸기보다 원래 객체의 내부 상태를 변경하게 만들고 싶을 때가 있다. 변경 가능한 컬렉션에 원소를 추가하는 경우가 대표적인 예다.

>>> val numbers = ArrayList<Int>()
>>> numbers += 42
>>> println(numbers[0])
42

반환 타입이 unit인 plusAssign 함수를 정의하면 코틀린은 += 연산자에 그 함수를 사용한다. 다른 복합 대입 연산자 함수도 비슷하게 minusAssign, timesAssign 등의 이름을 사용한다.

 

7.1.3 단항 연산자 오버로딩

단항 연산자를 오버로딩하는 절차도 이항 연산자와 마찬가지다. 미리 정해진 이름의 함수를(멤버나 확장 함수로) 선언하면서 operator로 선언하면 된다.

// 단항 연산자 정의하기
operator fun Point.unaryMinus(): Point { // 단항 minus 함수는 파라미터가 없다.
    return Point(-x, -y) // 좌표에서 각 성분의 음수를 취한 새 점을 반환한다.
}
>>> val p = Point(10, 20)
>>> println(-p)
Point(x=-10, y=-20)

단항 연산자를 오버로딩하기 위해 사용하는 함수는 인자를 취하지 않는다.

함수 이름
+a unaryPlus
-a unaryMinus
!a not
++a, a++ inc
--a, a-- dec

 

댓글