코틀린학습 - #6 오버로딩

2020. 11. 17. 15:13개발&TIL/kotlin

연산자 오버로딩과 기타 관례

미리 정해진 이름의 함수를 연결해주는 기법을 코틀린에서는 관례(convention)라고 부른다.

연산자를 오버로딩하는 함수 앞에는 꼭 operator 가 있어야 한다.

  • plus 연산자 구현하기
data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point {
        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 fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)  
}
  • 오버로딩 가능한 이한 산술 연산자
함수 이름
a * b times
a * b div
a % b mod(1.1부터 rem)
a + b plus
a - b minus

plus와 같은 연산자를 오버로딩하면 코틀린은 + 연산자뿐 아니라 그와 관련 있는 연산자인 +=도 자동으로 함께 지원한다.

+=, -= 등의 연산자는 복합 대입(compound assignment) 연산자 라 불린다.

변수가 변경 가능한 경우에는 복합 대입 연산자를 사용할 수 있다.

var point = Point(1, 2)  
point += Point(3, 4) // point = point + Point(3, 4)의 식과 같다.  
println(point)

단항 연산자를 오버로딩하는 절차도 미리 정해진 이름의 함수를 선언하면서 operator 를 표시하면 된다.

  • 단항 연산자 정의하기
operator fun Point.unaryMinus(): Point {  
    return Point(-x, -y)  
}
  • 오버로딩할 수 있는 단항 산술 연산자
함수 이름
+a unaryPlus
-a unaryMinus
!a not
++a, a++ inc
--a, a-- dec

비교 연산자 오버로딩 (equals, compareTo)

동등성 검사 ==는 equals() 호출과 널 검사로 컴파일된다.

식별자 비교연산자(===) 는 자바의 == 연산자와 같다. 따라서 두 피연산자가 같은 객체를 가르치는지 체크

equals 함수를 정의할때는 override 키워드를 붙여야 한다. Any에 정의된 메소드이므로 오버라이드가 필요

Any의 equals에 이미 operator가 붙어 있기때문에 때문에 operator를 붙이지 않음.

compareTo 생략

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

인덱스 연산자 : 인덱스를 사용해 원소를 설정하거나 가져오는것 a[b]

코틀린에서 인덱스 연산자도 관례를 사용하여 원소를 읽을때 get 연산자 메소드로 변환, 원소를 쓰는 연산은
set 연산자 메소드로 변환된다.

  • get 관례 구현하기
operator fun Point.get(index: Int): Int {  
    return when(index) {  
        0 -> x  
        1 -> y  
        else ->  
            throw java.lang.IndexOutOfBoundsException("Invalid index: $index")  
    }  
}

val p = Point(10, 20)  
println(p\[1\])  
//20

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

열린 범위는 끝 값을 포함하지 않는 범위를 말한다.

10..20 이라는 식은 10이상 20이하의 범위가 생긴다.

10 until 20 라는 식은 10이상 19이하인 범위를 만든다.

  • 날짜의 범위 다루기 (나중에 유틸로 사용가능할것 같다.)
val now = LocalDate.now()  
val vacation = now..now.plusDays(10) //now.rangeTo(now.plusDays(10)) 으로 변환  
println(now.plusWeeks(1) in vacation)  
//true

rangeTo 연산자는 다른 산술 연산자보다 우선순위가 낮으므로 괄호로 인자를 감싸주자

구조 분해 선언(destructuring declaration)과 component 함수

구조 분해를 사용하면 복합적인 값을 분해해서 한꺼에 초기화할 수 있다.

  • 구조 분해 사용하는 방법
val p = Point(10, 20)  
val (x, y) = p  
println(x)  
//10  
println(y)  
//20

내부에서 구조 분해 선언은 관례를 사용한다. 구조 분해 선언의 각 변수를 초기화하기 위해 componentsN이라는 함수를 호출한다.

구조 분해 선언은 componentN 함수 호출로 변환된다.

                val x = p.component1()
val(x, y) = p =>  
                val y = p.component2()

코틀린 표준 라이브로리에서는 맨 앞의 다섯 원소에 대해 componentN을 제공한다.

  • 구조 분해 선언을 사용해 맵 이터레이션하기
fun printEntries(map: Map<String, String>) {
    for ((key, value) in map) {
        println("$key -> $value")
    }
}

val map = mapOf("oracle" to "java", "JetBrains" to "Kotlin")
printEntries(map)
//oracle -> java
//JetBrains -> Kotlin

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

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

위임 프로퍼트의 일반적인 문법은 다음과 같다.번

Delegate 클래스는 getValue, setValue 메소드를 제공해야 한다.

class Delegate {
    operator fun getValue(...) { ... }
    operator fun setValue(..., value: Type) { ... }
}

class Foo {
    var p: Type by Delegate()
}

지연 초기화는 객체의 일부분을 초기화하지 않고 남겨뒀다가 실제로 그부분의 값이 필요할 경우
초기화할 때 사용하는 패턴이다.

뒷바침 프로퍼티(backing property) 기법
val, var 형태의 두개의 값을 가지고 저장 및 읽기 연산을 처리하는 방법.

위임 객체를 반환하는 표준 라이브러리 함수가 lazy다. lazy 함수는 기본적으로 스레드 세이프

  • 지연 초기화를 위임 프로퍼티를 통해 구현하기
class Person(val name: String) {
    val emails by lazy { loadEmails(this) }
}

by 키워드를 사용해 위임 객체를 지정하면 코틀린 컴파일러가 자동으로 처리해 준다.
(물론 getValue, setValue 가 선언되있는 객체이어야 한다.)

728x90

'개발&TIL > kotlin' 카테고리의 다른 글

코틀린학습 - #5 타입 시스템  (0) 2020.11.17
코틀린학습 - #4 람다  (0) 2020.11.16
코틀린학습 - #3 클래스  (0) 2020.11.11
코틀린학습 - #2 함수  (0) 2020.11.09
코틀린학습 - #1 기초  (0) 2020.11.09