산술 연산자 오버로드

유우선·2026년 2월 16일

Kotlin Study📚

목록 보기
21/32
  • 자바 → 기본 타입에 대해서 만 산술 연산자를 사용할 수 있음
    • String 값에 대해 + 연산자를 사용할 수 있음
  • 클래스에 메서드를 호출하는것 보다 산술 연산자를 사용하는 편이 편할 때가 있음
    • BigInteger 클래스에서 add를 호출하는것 보다 + 연산을 사용하는 편이 편함
  • 컬렉션에 원소를 추가하는 경우에도 +=을 사용할 수 있으면 편할 것

이항 산술 연산자 오버로딩 (plus, times, divide 등)

plus 연산자 오버로딩

data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
}

fun main() {
    val p1 = Point(10, 20)
    val p2 = Point(30, 40)
    println(p1 + p2)
    // Point(x=40, y=60)
}
  • 연산자 오버로딩은 operator 키워드를 붙여야 함
    • 어떤 함수가 관례를 따르는 함수임을 명확히 할 수 있음
    • 관례에서 사용하는 함수 이름을 사용하는 실수를 방지해줌

확장 함수로 연산자 오버로딩

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

operator fun Point.plus(other: Point): Point = Point(x + other.x, y + other.y)

fun main() {
    val p1 = Point(10, 20)
    val p2 = Point(30, 40)
    println(p1 + p2)
    // Point(x=40, y=60)
}
  • 연산자를 맴버 함수로 만드는 대신 확장 함수로 정의할 수 있음

다른 언어와 비교

  • 코틀린은 자신만의 연산자를 정의할 수 없음
    • 오버로딩한 연산자를 정의하고 사용하기 쉬움
  • 중위 함수를 제공함
    • a myOp b
    • 함수의 양변에 피연산자를 둘 수 있음

관례로 정의된 함수 이름과 연산자

  • a * b → times

  • a / b → div

  • a % b → mod

  • a + b → plus

  • a - b → minus

  • 오버로딩으로 함수를 구현해도 연산자 우선순위는 변하지 않음

서로 다른 타입의 피 연산자

  • 연산자를 정의할 때 두 피 연산자가 같은 타입일 필요는 없음
operator fun Point.times(scale: Double): Point = 
		Point((x * scale).toInt(), (y * scale).toInt())

fun main() {
    val p1 = Point(10, 20)
    println(p1 * 1.5)
    // Point(x=15, y=30)
}
  • 코틀린 연산자는 교환법칙을 자동으로 제공하지 않음
    • 교환 법칙 : a op b == b op a
  • 1.5 * p1을 지원하려면 순서에 대응하는 연산자 함수를 정의해줘야 함
operator fun Double.times(p: Point) : Point =
    Point((absoluteValue * p.x).toInt(), (absoluteValue * p.y).toInt())

fun main() {
    val p1 = Point(10, 20)
    println(1.5 * p1)
    // Point(x=15, y=30)
}

반환값의 타입

  • 반환 타입이 꼭 두 피연산자 중 하나와 일치해야 하는 것은 아님
operator fun Char.times(count: Int): String =
    toString().repeat(count)

fun main() {
    println('a' * 3)
    // aaa
}

operator 함수 오버로딩

  • 일반 함수처럼 operator 함수도 오버로딩 할 수 있음
  • 이름은 같지만 파라미터 타입이 서로 다른 연산자 함수를 여러 개 만들 수 있음

비트 연산자

  • 코틀린은 표준 숫자 타입에 대해 비트 연산자를 정의하지 않음
    • 커스텀 타입에서 비트 연산자를 정의할 수 없음
  • 대신 중위 연산자 표기법을 지원하는 일반 함수로 비트 연산을 수행
  1. shl : 왼쪽 시프트
  2. shr : 오른쪽 시프트 (부호 비트 유지)
  3. ushr : 오른쪽 시프트 (0으로 부호 비트 설정)
  4. and
  5. or
  6. xor
  7. inv : 비트 반전

복합 대입 연산자 오버로딩

  • 복합 대입 연산자 : +=, -= 등등…
  • 코틀린은 연산자 오버로딩하면 복합 대입 연산자를 자동으로 지원
operator fun Point.plus(other: Point): Point = 
		Point(x + other.x, y + other.y)

fun main() {
    var point = Point(1, 2)
    point += Point(3, 4)
    println(point)
    // Point(x=4, y=6)
}

복합 대입 연산자 커스텀하기

  • 반환 타입이 Unit인 plusAssign 함수를 정의하면서 operator 키워드를 사용
    • minusAssign, timesAssign 등도 있음
operator fun <T> MutableCollection<T>.plusAssign(element: T) {
		this.add(element)
}
  • 일반 연산자와 복합 대입 연산자를 동시에 오버로딩 해놓으면 컴파일러는 오류를 발생시킴

컬렉션에 대한 접근 방식

  1. +, -
    • 새로운 컬렉션 반환
  2. +=, -=
    • 메모리에 있는 객체 상태를 변화시킴
    • var로 선언한 읽기전용 컬렉션에만 적용할 수 있음

단항 연산자 오버로딩

operator fun Point.unaryMinus(): Point = Point(-x, -y)

fun main() {
    val p = Point(10, 20)
    println(-p)
    // Point(x=-10, y=-20)
}
  • 단항 연산자 오버로딩은 인자를 취하지 않음

단항 연산자 종류

  1. +a : unaryPlus
  2. -a : unaryMinus
  3. !a : not
  4. ++a, a++ : inc
  5. --a, a-- : dec
  • 증감 연산자(inc, dec)를 오버로딩 하는 경우 일반적인 값에 대해 전위/후위 연산자와 같은 의미를 제공함
import java.math.BigDecimal

operator fun BigDecimal.inc() = this + BigDecimal.ONE

fun main() {
    var bd = BigDecimal.ZERO
    println(bd++)
    // 0
    println(bd)
    // 1
    println(++bd)
    // 2
}

0개의 댓글