Operator-Overloading
1 Operator Overloading
- 레퍼런스
- 자바에는 표준 라이브러리와 연관된 언어 기능이 몇 가지 있다.
java.lang.Iterable(표준 라이브러리)을 구현한 객체를 for ... in 루프(언어 기능)에 사용할 수 있다.java.lang.AutoCloseable(표준 라이브러리)을 구현한 객체를 try문(언어 기능)에 사용할 수 있다.
- 코틀린에서도 어떤 언어 기능이 정해진 사용자 작성 함수와 연결되는 경우가 있다.
- 자바와 다르게 이런 언어 기능이 클래스와 연관되기보다는 특정 함수 이름과 연관된다.
- 예를 들어 어떤 클래스에 plus라는 이름의 특별한 메서드를 정의하면 해당 클래스의 인스턴스에
+연산자를 사용할 수 있다.
- 이런식으로 미리 정해진 이름의 함수와 어떤 언어 기능을 연결해주는 기법을 코틀린에서는 Convention이라고 부른다.
1.1 연산자 오버로딩을 사용하는 이유
- 자바에서는 원시 타입에 대해서만 이항 산술 연산자를 사용할 수 있고 추가적으로 String에 대해
+연산자를 사용할 수 있다. - 연산자 오버로딩이 가능하면 아래와 같은 것들이 가능하다.
- BigInteger의 경우 add 메서드를 명시적으로 호출하기보다는
+연산자를 사용하는 편이 더 직관적일 것이다. - 컬렉션에 원소를 추가하는 작업에도
+=연산자를 사용할 수 있다면 더 직관적일 것이다.
- BigInteger의 경우 add 메서드를 명시적으로 호출하기보다는
- 자바에서는 이런 일이 불가능 하지만 코틀린에서는 이런 일이 가능하다.
- 코틀린에서는 연산자와 매칭되는 특별한 함수 이름이 이미 정의되어 있다.
- 이것이 앞서 말한 Convention이다
- 따라서
+연산자와 매칭되는 특별한 함수 이름 plus라는 함수를 정의하면 plus 메서드를 명시적으로 호출하지 않고+연산자를 사용하면 컴파일러가plus메서드를 호출하는 코드로 변경해준다.
2 Binary Operators Overloading
2.1 Arithmetic Operators Overloading
- Convention의 가장 단순한 예시로는 이항 산술 연산자(Binary Arithmetic Operators)가 있다.
예시
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 point1 = Point(10, 20)
val point2 = Point(20, 30)
println(point1 + point2)
}
- 위와 같이 plus 함수 앞에 operator 키워드를 붙인다.
- 연산자를 오버로딩 하는 함수 앞에는 반드시 operator가 있어야 한다.
- operator를 생략하고 Convention에서 사용하는 함수 이름을 사용하면 아래와 같은 오류가 발생한다.
'operator' modifier is required on 'plus' in '...'
- 이제
point1 + point2을 사용하면 컴파일 시점에point1.plus(point2)`로 치환된다.
출력 결과는 아래와 같다.
Point(x=30, y=50)
Arithmetic operations Convention
- 코틀린에서 정의할 수 있는 이항 연산자와 그에 상응하는 연산자 함수 이름은 아래와 같다.
| Expression | Translated to |
|---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b) |
a..b | a.rangeTo(b) |
a..<b | a.rangeUntil(b) |
다른 타입의 두 피연산자
- 연산자를 정의할 때 두 피연산자가 같은 타입일 필요는 없다.
- 아래의 예시를 보자.
operator fun Point.times(scale: Double): Point {
return Point((x * scale).toInt(), (y * scale).toInt())
}
fun main() {
val point1 = Point(10, 20)
println(point1 * 1.5)
}
- 코틀린 연산자를 자동으로 교환 법칙이 적용되지 않는다.
point1 * 1.5외에1.5 * point1라고 쓸 수 있어야 된다면 연산자 오버로딩을 반대로 한번 더 해야한다.
출력 결과는 아래와 같다.
Point(x=15, y=30)
2.2 Augmented assignments Operators Overloading
- 복합 대입 연산자 오버로딩에 대해서 알아보자.
plus와 같은 연산자를 오버로딩하면 코틀린은+연산자뿐 아니라 그와 관련 있는 연산자인+=도 자동으로 함께 지원한다.+=,-=등의 연산자를 복합 대입 연산자라 부른다.
Augmented assignments Convention
| Expression | Translated to |
|---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b) |
주의점
- 이론적으로 코드에 있는
+=은 아래와 같이plus와plusAssign양쪽으로 컴파일이 가능하다.a = a.plus(b)a.plusAssign(b)
- 따라서 plus와 plusAssign 연산을 동시에 정의하지 말자.
- 만약 앞에서 본 Point 처럼 변경이 불가능하다면 plus와 같이 새로운 값을 반환하는 연산만 추가해야 한다.
- 빌더와 같이 변경 가능한 클래스를 설계한다면 plusAssign을 정의하자.
복합 대입 연산자와 컬렉션
- 컬렉션과 연산자를 같이 사용할 때 어떻게 동작하는지 알아보자.
+와-연산자를 사용하면 항상 새로운 컬렉션을 반환한다.+=와-=연산자는 항상 변경 가능한 컬렉션에 작용해 메모리에 있는 객체 상태를 변화시킨다.- 새로운 컬렉션을 만들어 반환하지 않는다.
- 읽기 전용 컬렉션에
+=와-=를 사용하면 변경을 적용한 복사본을 반환한다.- 새로운 컬렉션을 만들어 반환한다.
- 따라서 var로 선언한 변수가 가리키는 읽기 전용 컬렉션에만
+=와-=를 사용할 수 있다.
2.3 Equality and inequality operators Overloading
- 비교 연산자 오버로딩에 대해 알아보자!
- 코틀린은
==연산자 호출을equals메서드 호출로 컴파일 한다. a == b연산자는 먼저 a가 널인지 판단해서 널이 아닌 경우에만 a.equals(b)를 호출한다. a가 널인 경우 b도 널인 경우에만 true가 반환된다.
Equality and inequality operators convention
| Expression | Translated to |
|---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |