[Kotlin] 함수(2)

sw·2022년 1월 4일
0

1. 확장함수

어떤 클래스의 멤버 메소드인 것처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수이다.

  • 확장 함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이기만 하면 된다.
fun String.lastChar(): Char = this.get(this.length-1)
// this 생략 가능
fun String.lastChar(): Char = get(length-1)

>>> println("abc".lastChar())	// "abc"는 수신 객체, String은 수신 객체 타입
c
  • 수신 객체 타입 (receiver type) : 확장이 정의될 클래스
  • 수신 객체 (receiver object) : 그 클래스에 속한 인스턴스 객체
  • 확장 함수가 캡슐화를 깨진 않는다
    -> 클래스 내부에서만 사용할 수 있는 private이나 protected로 선언된 멤버는 사용할 수 없다.

2. import와 확장 함수

  • 확장 함수를 사용하기 위해서는 다른 클래스나 함수와 마찬가지로 import해야 한다.
  • as 키워드를 사용하면 import한 클래스나 함수를 다른 이름으로 부를 수 있다. (다른 여러 패키지에 속해있는 이름이 같은 함수를 가져와 사용해야 하는 경우 충돌 방지)
import strings.lastChar as last

val c = "Kotlin".last()

자바에서 확장 함수 호출

  • 내부적으로 확장 함수는 수신 객체를 첫 번째 인자로 받는 정적 메소드(static method)이다. 그래서 확장 함수를 호출해도 부가 비용이 들지 않는다.
  • static method를 호출하면서 첫 번째 인자로 수신 객체를 넘기기만 하면 된다.
/* 자바 */
// 확장 함수를 StringUtil.kt파일에 정의
char c = StringUtilKt.lastChar("Java");

3. 확장 함수로 유틸리티 함수 정의

fun <T> Collection<T>.joinToString(
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
        if (index > 0) result.append(separator)
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}

>>> val list = arrayListOf(1, 2, 3)
>>> println(list.joinToString(" ")
1 2 3


// 구체적인 타입을 수신 객체 타입으로 지정할 수도 있다.
fun Collection<String>.join(
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
) = joinToString(separator, prefix, postfix)

>>> println(listOf("one", "two", "eight").join(" "))
one two eight

4. 확장 함수는 오버라이드할 수 없다.

Method Dispatch : 어떤 메소드를 호출할지 결정해서 실행시키는 과정

  • Static Dispatch: 컴파일 시점에서 컴파일러가 특정 메소드를 호출할 것이라고 명확히 알고 있는 경우이다.
  • Dynamic Dispatch: 컴파일러가 어떤 메소드를 호출할 지 모르는 경우이다. 호출할 메소드를 런타임 시점에 결정한다.
  • 동적 디스패치 예시
open class View{
    open fun click() = println("View Clicked")
}

class Button: View(){
    override fun click() = println("Button Clicked")
}

>>> val view: View = Button()
>>> view.click()
Button Clicked
  • 이에 반해, 확장 함수는 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장 함수가 호출될지 결정된다. 코틀린은 호출될 확장 함수를 static하게 결정하기 때문에 오버라이드 할 수 없다.
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")

>>> val view: View = Button()
>>> view.showOff()
I'm a view!

5. 확장 프로퍼티

  • 상태를 저장할 적절한 방법이 없기 때문에 실제로 아무 상태도 가질 수 없다. 초기화도 불가능!
  • getter는 필수적으로 정의 해야 한다.
val String.lastChar: Char
    get() = get(length-1)
    
var StringBuilder.lastChar: Char
    get() = get(length-1)
    set(value: Char){
        this.setCharAt(length-1, value)
    }
profile
끄적끄적

0개의 댓글