연산자 오버로딩
•
Kotlin에서는 타입에 대해 미리 정의된 연산자 집합의 사용자 정의 구현을 제공할 수 있다.
•
이 연산자는 기호 표현(+ 또는 *) 및 우선순위를 가지고 있다.
•
연산자를 구현하려면 해당 타입에 대한 특정 이름을 가진 멤버 함수 또는 확장 함수를 제공하면 된다.
•
이때 이 타입은 이항 연산자의 왼쪽 타입이 되며 단항 연산자의 경우 인수 타입이 된다.
interface IndexedContainer {
operator fun get(index: Int)
}
Kotlin
복사
•
연산자를 오버로드하려면 해당 함수에 operator 수정자를 표시한다.
class OrdersList: IndexedContainer {
override fun get(index: Int) { /*...*/ }
}
Kotlin
복사
•
연산자 오버로드를 재정의할 때는 operator를 생략할 수 있다.
단항 연산
단항 접두사 연산자
표현식 | 변환된 코드 |
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
•
+a 표현식을 컴파일러가 처리하는 과정
1.
a의 타입을 확인하고 T라고 가정한다.
2.
수신자가 T인 unaryPlus() 함수를 찾는다.
3.
함수가 없거나 모호할 경우 컴파일 오류가 발생한다.
4.
함수가 존재하고 반환 타입이 R이라면 표현식 +a는 타입 R을 가진다.
•
-a 표현식을 오버로드 하는 예시
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
fun main() {
println(-point) // 출력: "Point(x=-10, y=-20)"
}
Kotlin
복사
증가 및 감소 연산자
표현식 | 변환된 코드 | 기타 |
a++ | a,inc() + (아래 참조) | 값을 반환해야 하며 이 값은 ++ 또는 -- 연산자를 사용된 변수에 할당되고 객체 자체를 변경하지 않아야 한다. |
a-- | a.dec() + (아래 참조) | 값을 반환해야 하며 이 값은 ++ 또는 -- 연산자를 사용된 변수에 할당되고 객체 자체를 변경하지 않아야 한다. |
•
a++ 표현식을 컴파일러가 처리하는 과정
1.
a의 타입을 확인한다.
2.
수신자가 T인 inc() 함수를 찾는다.
3.
함수의 반환 타입이 T의 하위 타입인지 확인한다.
•
a++ 표현식의 연산 과정
◦
a의 초기 값을 임시 저장소에 저장한다.
◦
a.inc()의 결과를 a에 할당한다.
◦
표현식의 결과로 초기 값 a0을 반환한다.
•
전위 연산자와 후위 연산자의 차이
◦
전위 연산자는 새로 계산된 값을 반환하고 후위 연산자는 초기 값을 반환한다.
이항 연산
산술 연산자
표현식 | 변환된 코드 |
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) |
•
+ 연산자를 오버로드한 Counter 클래스 예시
data class Counter(val dayIndex: Int) {
operator fun plus(increment: Int): Counter {
return Counter(dayIndex + increment)
}
}
Kotlin
복사
in 연산자
표현식 | 변환된 코드 |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
•
in과 !in의 절차는 동일하지만 인수의 순서가 반대이다.
인덱스 접근 연산자
표현식 | 변환된 코드 |
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, [i_n] = b | a.set(i_1, …, i_n, b) |
•
대괄호는 적절한 개수의 인수를 포함하는 get, set 호출로 변환된다.
호출 연산자
표현식 | 변환된 코드 |
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
•
괄호는 적절한 개수의 인수를 갖는 invoke 호출로 변환된다.
증가된 할당
표현식 | 변환된 코드 |
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timeAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b) |
•
a += b 표현식을 컴파일러가 처리하는 과정
◦
plusAssign() 함수가 사용 가능하다면
1.
plus(), plusAssign()이 둘 다 사용가능하고 a가 변경 가능한 함수이고 plus()의 반환 타입이 a의 하위 타입이라면 오류 발생
2.
반환 타입이 Unit 인지 확인하고 그렇지 않으면 오류 발생
3.
a.plusAssign()에 대한 코드 생성
◦
plusAssign() 함수가 사용 가능하지 않다면
1.
a = a + b 에 대해 a + b 의 타입이 a의 하위 타입인지 검사하고 코드를 생성하려고 시도
Kotlin에서 할당은 표현식이 아니다.
동등 및 부등 연산자
표현식 | 변환된 코드 |
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
•
== 와 != 연산자는 두 객체의 동등성을 비교하는 연산자로 equals(other: Any?): Boolean를 호출하여 동작한다.
•
=== 와 !== 연산자는 두 객체의 참조 동일성을 비교하는 연산자로 오버로딩이 불가능하다.
•
null == null 은 항상 동등하다고 간주되며 x == null 에서 x가 null이 아니면 equals()가 호출되지 않고 false를 반환한다.
비교 연산자
표현식 | 변환된 코드 |
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
•
모든 비교는 Int 반환이 필요한 compareTo로 변환된다.
프로퍼티 위임 연산자
•
명명된 함수에 대한 infix 호출
•
Functions 에 infix 함수를 호출함으로써 커스텀 infix 함수를 시뮬레이션 할 수 있다.