본문 바로가기
공부/Kotlin

[Kotlin] 연산자 오버로딩과 기타 관례 사용해보기

by JERO__ 2022. 6. 20.

1. 산술 연산자 오버로딩

자바 : 원시타입, String 만 가능하다.

코틀린 : 클래스, 컬렉션에도 적용 가능

1-1. 이항 산술 연산 오버로딩 + - * / %

클래스 내부에 operator fun 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(40, 60)
															// 실제로, p1.plus(p2) 로 호출된다.
}

식 함수이름

특징

  • 연산자 우선순위와 같게 진행된다
  • *, /, % 우선순위가 같고 +, -가 같다
  • 두 피연산자가 같은 타입일 필요 없다.

1-2. 복합 대입 연산자 += -=

연산자 오버로딩(+, -…)을 하면 연산자 뿐 아니라 관련있는 +=, -= 등도 자동으로 지원한다.

 
fun main() {
	var p1 = Point(10, 20)
	var p2 = Point(30, 40)
	p1 += p2
	println(p1)            // Point(40, 60)
}

컬렉션에서의 사용

fun main() {
    val numbers = mutableListOf<Int>()
    numbers += 1
    numbers += 2
    numbers += 3
    numbers -= 2
		println(numbers)       // [1, 3]
}
  • 없을때 빼도 오류가 나지 않는다!

1-3. 단항 연산자 오버로딩

이항 연산 : a + b

단항 연산 : -a

operator fun Point.unaryMinus(): Point {
	return Point(-x, -y)
}

fun main() {
	val p = Point(10, 20)
	print(-p)                   // Point(-10, -20)
}

오버로딩할 수 있는 단항 산술 연산자

식 함수이름

+a unaryPlus
-a unaryMinus
!a not
++a, a++ inc
—a, a— dec

2. 비교 연산자 오버로딩

2-1. 동등성 연산자 ==

코틀린의 ==, != 연산자는 equals 메서드 호출로 컴파일한다.

  • 사용은 ==
  • 오버리이딩은 equals
class Point(val x:Int, val y:Int) {
	override fun equals(obj: Any?): Boolean {
		if (obj === this) return true
		if (obj !is Point) return false
		return obj.x == x && obj.y == y
	}
}

식별자비교(===) : 자바의 ==과 같다. 식별자비교는 오버로딩 할 수 없다.

2-2. 순서 연산자 >=

자바에서 정렬, 최대값, 최소값을 비교할 때 Comparable 인터페이스를 구현해야 한다. 코틀린도 마찬가지

  • 사용 : >=
  • 오버라이딩 : compareTo
class Person(val firstName: String, val lastName: String) : Comparable<Person>{
	override fun compareTo(other: Person): Int {
		return compareValuesBy(this, other, Person::lastName, Person::firstName)
	}
}

3. 컬렉션과 범위에 대해 쓸 수 있는 관례

3-1. 인덱스로 원소에 접근 [] ⇒ get set

  • 사용 : val value = map[key]
    • 사용시 get 연산자 메서드로 변환되고 쓰는 연산은 set 연산자 메서드로 변환된다.

3-2. in 관례 contains

in 연산자와 대응하는 함수는 contains다.

3-3. .. 관례 rangeTo

1..10 구문 : 1부터 10까지 모든 수가 들어있는 범위를 가리킨다.

start..end → start.rangeTo(end)

3-4. for 루프를 위한 iterator 관례

iterator : 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화한 것

코틀린에서도 iterator 메서드를 확장함수로 정의할 수 있다.

4. 구조 분해 선언과 component 함수

구조 분해 선언

val (a, b) = p
// val a = p.component1()
// val b = p.component2()
  • component1 확장함수가 제공됨 (코틀린 라이브러리)

5. 프로퍼티 접근자 로직 재활용: 위임 프로퍼티

프로퍼티는 위임을 사용해 자신의 값을 필드가 아닌 DB나 브라우저 세션, 맵 등에 저장할 수 있다.

위임 : 객체가 직접 작업을 수행하지 않고 다른 도우미 객체가 그 작업을 처리하게 맡기는 디자인 패턴

1. 프로퍼티 초기화 지연 by lazy()

지연 : 실제고 그 부분의 값이 필요한 경우 초기화할 때 쓰임

class Person(val name: String) {
	val emails by lazy { loadEmails(this) }
}

2. 위임 프로퍼티 구현

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
    fun removePropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.removePropertyChangeListener(listener)
    }
}
class ObservableProperty(var propValue: Int, val changeSupport: PropertyChangeSupport) {
    operator fun getValue(p: Person, prop: KProperty<*>): Int = propValue
    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}

class Person (val name:String, age:Int, salary: Int) : PropertyChangeAware() {
    var age: Int by ObservableProperty(age, changeSupport)
    var salary: Int by ObservableProperty(salary, changeSupport)
}

 

kotlin in action 책을 통한 공부입니다.

 

댓글