3장. 함수 정의와 호출

dev.jhjhj·2022년 4월 3일
0

Kotlin In Action

목록 보기
3/3

3.1 코틀린에서 컬렉션 만들기

코틀린 컬렉션은 자바 컬렉션과 똑같은 클래스이다. 표준 자바 컬렉션을 활용하면 자바 코드와 상호 작용하기 훨씬 더 쉽다. 각각의 언어에서 호출할 때, 컬렉션을 서로 변환할 필요가 없다.

But, 코틀린에서는 자바보다 더 많은 기능을 쓸 수 있다.

val strings = listOf ("first”, "second”, ”fourteenth")
printin (strings. last ()) // fourteenth

val numbers = setOf (1, 14, 2)
printin (numbers.max())  // 14

3.2 함수를 호출하기 쉽게 만들기

자바 컬렉션에는 기본적으로 toString 구현이 들어있다. 코틀린을 이용해서 joinTostring 함수 구현하기

// joinToString() 함수의 초기 구현
//<T> 제네릭 타입
fun <T> joinToString(
    collection: Collection<T>,
    separator: String,
    prefix: String, postfix: String
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

3.2.1 이름 붙인 인자

코틀린에서는 함수를 호출할 때, 인자에 이름을 붙여서 호출이 가능하다.

호출시 인자 중 어느 하나라도 이름을 명시한다면, 혼동을 막기 위해 그 뒤에 오는 모든 이니자의 이름을 꼭 명시해야한다.

fun main() {
    val list = listOf(1, 2, 3)
    println(joinToString(list, ";", "(", ")"))

// 이름을 붙인 인자
    println(joinToString(list, separator = ";", prefix = "(", postfix = ")"))
}

3.2.2 디폴트 파라미터 값

자바에서는 일부 클래스에서 오버로딩한 메소드가 너무 많다. 그렇기 때문에 개발자들이 반복적인 설명 주석을 달아야하는 경우가 많다.

코틀린에서는 함수를 선언할 때, 파라미터의 디폴트 값을 지정할 수 있어서, 이런 오버로드를 피할 수 있다.

fun <T> joinToString(
    collection: Collection<T>,
// 디폴트 값이 지정된 파라미터들
    separator: String = ",",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)
    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)
        result.append(element)
    }

    result.append(postfix)
    return result.toString()
}

println(joinToString(list, ";", "(", ")"))
println(joinToString(list))
println(joinToString(list, "; "))

자바에는 디폴트 파라미터 값이라는 개념이 없다. 코틀린 함수를 자바에서 호출하라 경우, 코틀린 함수가 디폴트 파라미터 값을 제공하더라고 모든 인자를 명시해야 한다.

자바에서 코틀린 함수를 자주 호출해야한다면, @JvmOverloads 애노테이션을 함수에 추가

@JvmOverloads 를 추가하면, 코틀린 컴파이리러가 자동으로 맨 마지막 파라미터부터 파라미터를 하나씩 생략한 오버로딩한 자바 메소드를 추가해준다.

3.2.3 정적인 유틸리티 클래스 없애기 : 최상위 함수와 프로퍼티

자바에서는 모든 코드를 클래스의 메소드로 작성해야하는 데, 실전에서는 어느 한 클래스에 포함하기 어려운 코드들이 많다. 그래서 Utill 클래스를 만들기도 한다.

하지만, 코틀린에서는 이런 고민을 할 필요가 없다.

→ 함수를 직접 소스파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 된다.

  • JVM은 새로운 클래스로 정의해주며, Java와 비교하자면 ... static 함수로 선언
package strings;

public class JoinKt {
		public static String joinToString (. . .) { ... }
}

최상위 프로퍼티

함수와 마찬가지로 프로퍼티도 파일의 최상위 수준에 놓을 수 있다.

이런 프로퍼티 값은 정적 필드에 저장된다.

val opCount=0   // 최상위 프로퍼티 선언

fun performOperation() {
	opCount++   // 최상위 프로퍼티 값 변경
  // ...
}

fun reportOperationCount() {
  println("Operation performed $opCount times")  // 최상위 프로퍼티 값 읽기
}

const 키워드를 사용하면 java의 상수, public static final 필드로 컴파일하게 만들 수 있다. (단, primitive type과 String 타입의 프로퍼티만 가능)

3.3 메서드를 다른 클래스에 추가 : 확장 함수와 확장 프로퍼티

확장함수란 ?

  • 어떤 클래스의 멤버 메소드처럼 호출할 수 있지만, 클래스 밖에서 선언된 함수
  • 확장함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확잘할 클래스의 이름을 덧붙인다.
  • 클래스 이름을 수신 객체 타입, 확장 함수가 호출되는 대상이 되는 값을 수신 객체

package strings
fun String.lashChar(): Char = this.get(this.length - 1)

println("Kotlin".lastChar())

String이 수신 객체 타입, “Kotlin”이 수신 객체

  • 확장 함수 내부에서는 수신 객체의 메소드나 프로퍼티를 사용할 수 있다.
  • but, 캡슐화는 지킨다 → 클래스 내부에서만 사용할 수 있는 private 멤버나 protected 멤버를 사용할 수 없다.

3.3.1 임포트와 확장 함수

확장 함수를 사용하기위해서는 import를 해줘야만 한다.

코틀린 문법상 확장 함수는 반드시 짧은 이름을 써야한다. import 할 때 이름을 바구는 것이 확장 함수 이름을 충돌할 수 있는 유일한 방법이다.

import strings.lastChar
// import strings.*
// import string.lastChar as last   // 별칭 사용 가능

val c = "Kotlin".lastChar()
// val c = "Kotlin".last()

3.3.2 자바에서 확장 함수 호출

확장함수는 수신 객체를 첫번째로 받는 정적 메소드이다. 이런 설계로 확장 함수를 자바에서 사용하기 편하다. 확장함수를 StringUtil.kt 파일에 정의했다면 다음과 같이 호출할 수 있다.

char c = StringUtilKt.lastChar("Java");

3.3.3 확장 함수로 유틸리티 함수 정의

joinsToString 함수 확장함수로 만들어서 코틀린 라이브러리가 제공하는 함수처럼 만들기

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()
}

fun main() {
    val list = listOf(1, 2, 3)
    println(list.joinToString("; ", "(", ")"))
		// (1; 2; 3)
}

확장 함수는 단지 정적 메소드 호출에 대한 문법적인 편의일 뿐이다. 클래스가 아닌 더 구체적인 타입을 수신 객체 타입으로 지정할 수 있다.

따라서 문자열의 컬렉션에 대해서만 호출할려면 다음과 같이 정의하면 된다.

fun <String>.joinToString(
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
) = joinToString(separator, prefix, postfix)

fun main() {
    printin (listOf ("one", "two", "eight").join (" "))
		// (1; 2; 3)
}

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

확장함수는 정적 메소드 (static)특징을 가지고 있기 때문에 오버라이드를 할 수 없다.

3.3.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)
    }

val sb = StringBuilder("Kotlin?")
    sb.lastChar = '!'
    println(sb) // Kotlin!

3.4 컬렉션 처리 : 가변 길이 인자, 중위 함수 호출, 라이브러리 지원

3.4.1 자바 컬렉션

코틀린은 자바 컬렉션 라이브러리를 확장해서 사용한다.

3.4.2 가변 인자: 인자의 개수가 달라질 수 있는 함수

//리스트 함수는 원하는 만큼 원소를 인자 값으로 전달할 수 있다.
val lislt = listOf(2, 3, 5, 7, 11)

fun listOf<T>(vararg values: T): List<T> {...}

자바의 가변길이 인자 → 키워드 ... ex) String ...str

코틀린의 가변길이 인자 varargs ex ) vararg values : T

이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때

자바의 경우 → 배열을 그냥 넘긴다.

코틀린의 경우 → 배열을 명시적으로 풀어서 배열의 각 원소가 인자로 전달되게 한다. 스프레드 연산자 사용 *

fun main(args: Array<String>){
	val list = listOf("args: ", *args)
	println(list)
}

3.4.3 값의 쌍 다루기 : 중위 호출과 구조 분해 선언

중위 호출 : to

수신객체 (공백) to

인자가 하나뿐인 일반 메소드, 인자가 하나뿐인 확장 함수에 중위호출 사용 가능

함수에 infix 키워드를 붙이면, 중위 호출해서 사용 가능하다.

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

1.to("one")  // 'to' 메소드 일반적인 호출 방식
2.to "two"  // 'to' 메소드를 중위 호출 방식으로 호출

infix fun Any.to(other: Any) = Pair(this, other)
val (number, name) = 1 to "one"  // 구조분해

for((index, element) in collection.withIndex()){
	println("$index: $element")
}

참고 : Pair 는 코틀린의 표준 라이브러리

to 함수를 이용해서 순서쌍을 만든다음 구조 분해를 통해, 그 순서쌍을 풀었다.

출처 : (서적) Kotlin in Action 드미트리 제메로프, 스베트라나 이사코바 저자 /오현석 옮김

profile
어제보다 더 나은

0개의 댓글