CS/코틀린 인 액션

3. 함수 정의와 호출

1. 코틀린에서 컬렉션 만들기

코틀린에서 List, Array 같은거 쓰면 자바의 것을 쓴다. 이는 자바로 정의하는게 호환성에 더 유리해서.

val set = hashSetOf(1, 7, 53)
println(set.javaClass)
>>> class java.util.HashSet

거기다 코틀린 자체적으로 확장 함수를 제공. 이건 좀 이따 자세한 설명 해줌

val numbers = listOf<Int>(1, 7, 53)
println(set.first())
println(set.max())
println(set.last())

 

2. 함수를 호출하기 쉽게 만들기

함수에 default 인자 설정 가능(놀랍게도 자바에는 없다).

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

만약 자바라면?? overloading을 많이 하면 된다..

그래도 나름 편하게 하라고 @JvmOverloads 하면 자동 생성됨.

String joinToString(Collection<T> collection, String separator, String prefix, String postfix);
String joinToString(Collection<T> collection, String separator, String prefix);
String joinToString(Collection<T> collection, String separator);
String joinToString(Collection<T> collection);

디폴트 인자는 함수 안에서 직접 정의해서 사용해야 됨.

 

 

클래스 밖에서도 변수와 함수 정의 가능.

원리는 자바에서도 쓸 수 있도록 코틀린 자체가 JoinKT 라는 static 클래스를 만들어 함수, 변수 정의하기 때문.

package strings
fun joinToString(...): String { ... }
package strings;

public class JoinKt {
    public static String joinToString(...) { ... }
}
 
========
import strings.JoinKt;
...
JoinKt.jlinToString(list, ", ", "", "");

이 JoinKT가 싫으면 코틀린 파일 맨 앞에 @file:어쩌구 붙이면 됨

@file:JvmName("StringFunctions")
package strings
fun joinToString(...): String { ... }

// 자바
import strings.StringFunctions;
StringFunctions.joinToString(list, ", ", "", "");

 

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

기존 자바 API에 확장 함수를 정의해서 원래 있던 것 처럼 사용할 수 있다.

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

println("Kotlin".lastChar()) // n
import strings.lastChar as last

val c = "Kotlin".last()

확장 함수는 오버라이딩 불가능. 얘내도 외부에서 정의해서 모방하는 형식이라. 확장 함수는 클래스 밖에 선언된다.

 

프로퍼티도 선언 가능

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char) {
        this.setCharAt(length - 1, value)
    }
println("Kotlin".lastChar) // n
val sb = StringBuilder("Kotlin?")
sb.lastChar = '!'
println(sb) // Kotiln!

 

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

+, -, to 이런 중간에 들어가는 함수 처리.

얘내들은 for문 같은 loop 돌때 쌍으로 출력 처리 가능(파이썬처럼)

val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
1.to("one")
1 to "one"
infix fun Any.to(other: Any) = Pair(this, other)
val (numer, name) = 1 to "one"

 

 

5. 문자열과 정규식 다루기

자바에서 문자열 split 안에 들어가는 건 일반 string이 아니라 정규식이 들어가는 거라 혼란은 야기함.

코틀린은 .toRegex()로 명시해줘야 하기 때문에 그럴 일이 없다.

println("12.345-6.A".split("\\.|-".toRegex())) // [12, 345, 6, A]
println("12.345-6.A".split(".", "-")) // [12, 345, 6, A]

fun parsePath(path: String) {
    val directory = path.substringBeforeLast("/")
    val fullName = path.substringAfterLast("/")
    val fileName = fullName.substringBeforeLast(".")
    val extension = fullName.substringAfterLast(".")
    println("Dir: $directory, name: $fileName, ext: $extension")
}

fun parasePath(path: String) {
    val regex = """(.+)/(.+)\.(.+)""".toRegex()
    val matchResult = regex.matchEntire(path)
    if (matchResult != null){
        val (directory, fileName, extension) = matchResult.destructured
        println("Dir: $directory, name: $fileName, ext: $extension")
    }
}

 

 

6. 코드 다듬기: 로컬 함수와 확장

함수 안에 함수 정의해서 더 깔끔하게 한다 그런 뜻임.

class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException(
                "Can't save user $id: empty $fieldName" // User 클래스 안의 정의라 $id라고 해도 알아들음
            )
        }
    }
    validate(name, "Name")
    validate(address, "Address")
}

fun saveUser(user: User) {
    user.validateBeforeSave()
    // Save user to the database
}