Kotlin in Action : 함수 정의와 호출

한포도·2024년 11월 4일
0

About Kotlin

목록 보기
3/6
post-thumbnail

3장에서 다루는 내용

  • 컬렉션, 문자열, 정규식을 다루기 위한 함수
  • 이름 붙인 인자, 디폴트 파라미터 값, 중위 호출 문법 사용법
  • 확장 함수와 확장 프로퍼티로 자바 라이브러리 적용하기
  • 최상위 및 로컬 함수와 프로퍼티를 사용한 코드 구조화

3.1 Kotlin에서 컬렉션 만들기

Kotlin에서는 자바와 동일한 List, Set, Map 컬렉션 클래스를 활용합니다. 자바 컬렉션을 그대로 활용하기 때문에 변환 없이 상호작용이 가능하며, 컬렉션 생성을 위한 다양한 함수들을 제공하여 코드를 간결하게 작성할 수 있습니다.

val set = hashSetOf(1, 7, 53)
val list = arrayListOf(1, 7, 53)
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")

Kotlin은 자바 컬렉션과 동일한 클래스 타입을 사용하지만, last()max() 등의 편리한 확장 함수도 제공합니다.


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

3.2.1 이름 붙인 인자

Kotlin은 함수 호출 시 파라미터 이름을 명시하여 가독성을 높이고 혼동을 줄일 수 있습니다. 각 인자의 이름을 붙여서 호출할 수 있으며, 이는 불리언 플래그나 기본값이 있는 파라미터에 특히 유용합니다.

joinToString(collection, separator = ", ", prefix = "(", postfix = ")")

3.2.2 디폴트 파라미터 값

Kotlin에서는 파라미터의 기본값을 지정할 수 있어 자바에서 흔한 오버로딩 문제를 줄일 수 있습니다. 이를 통해 호출 시 특정 인자를 생략할 수 있습니다.

fun <T> joinToString(
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String

3.2.3 정적 유틸리티 클래스 제거

자바에서는 정적 유틸리티 클래스에 많은 메서드를 모아놓지만, Kotlin에서는 최상위 함수로 직접 정의하여 클래스를 생략하고 패키지 수준에서 접근할 수 있습니다.

package strings

fun joinToString(...) { ... }

@JvmName: 최상위 함수가 포함될 클래스의 이름을 바꾸고 싶다면 @JvmName 애노테이션을 추가할 수 있다.


3.3 확장 함수와 확장 프로퍼티

3.3.1 확장 함수

Kotlin은 기존 클래스에 메소드를 추가하는 확장 함수를 지원합니다. 이를 통해 자바의 기존 클래스에도 Kotlin 스타일의 함수를 적용할 수 있습니다.

fun String.lastChar(): Char = this.get(this.length - 1)
println("Kotlin".lastChar())  // 'n'

확장 함수는 실제로 수신 객체를 첫 번째 인자로 받는 정적 메소드입니다. 또한 수신 객체의 메소드 및 프로퍼티를 사용할 수 있지만 클래스의 비공개 멤버에는 접근할 수 없습니다.

3.3.2 확장 프로퍼티

확장 함수와 마찬가지로 기존 클래스에 프로퍼티 형태의 기능을 추가할 수 있습니다. 확장 프로퍼티는 상태를 가질 수 없지만 기존 클래스에 읽기/쓰기 용도로 사용 가능합니다.

val String.lastChar: Char
    get() = get(length - 1)

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char) {
        this.setCharAt(length - 1, value)
    }

3.4 컬렉션 처리

3.4.1 가변 길이 인자

Kotlin의 vararg 키워드를 사용하면 가변 길이 인자를 받는 함수를 정의할 수 있습니다. 여러 개의 값을 가변 인자로 받아 배열로 변환할 수 있습니다.

fun listOf<T>(vararg values: T): List<T> { ... }
val list = listOf(2, 3, 5, 7, 11)

스프레드 연산자를 사용하여 배열을 가변 인자로 전달할 수 있습니다.

val args = arrayOf("args")
val list = listOf("first", *args)

3.4.2 중위 호출 및 구조 분해 선언

중위 호출은 단일 인자를 가진 메서드 호출을 보다 읽기 쉽게 해줍니다. 구조 분해 선언은 복합적인 값을 분해하여 여러 변수에 나눠 담을 수 있습니다.

val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
val (number, name) = 1 to "one"

to 함수는 실제로 Pair의 인스턴스를 반환하며, 중위 호출을 사용하여 Pair 객체를 생성할 수 있습니다.


3.5 문자열과 정규식

3.5.1 문자열 나누기

Kotlin에서는 split 확장 함수로 문자열을 쉽게 분할할 수 있으며, 정규식을 파라미터로 사용하면 더욱 유연하게 문자열을 다룰 수 있습니다.

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

3.5.2 3중 따옴표 문자열

3중 따옴표 문자열(""")을 사용하면 문자열을 여러 줄로 정의할 수 있습니다. 이를 통해 줄 바꿈과 들여쓰기를 자유롭게 조정할 수 있습니다.

val kotlinLogo = """
    .| //
    .|//
    .|/ \\
    """.trimMargin(".")

3중 따옴표 문자열은 텍스트에서 줄 바꿈을 포함할 때 유용하며, 테스트에서 여러 줄 출력 예상 값을 사용할 때도 편리합니다.


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

Kotlin에서는 함수 내부에 로컬 함수를 선언하여 중복을 제거하고 코드의 가독성을 높일 수 있습니다. 로컬 함수는 바깥 함수의 변수에 접근할 수 있습니다.

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

fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
        }
    }

    validate(user.name, "Name")
    validate(user.address, "Address")
}

로컬 함수를 활용하면 검증 로직과 같은 반복 코드를 줄이고 코드의 가독성을 개선할 수 있습니다.


이 뒤에는 제가 스터디에서 발표로 준비했던 자료인데 추가적으로 알아두면 좋을 것 들을 준비했습니다!

Kotlin에서 컬렉션 사용하기

1. 컬렉션 개념

  • 정의: 컬렉션은 여러 데이터를 모아 하나의 객체로 관리할 수 있는 자료 구조로, 대표적으로 리스트(List), 집합(Set), 맵(Map)을 포함합니다.
  • 주요 기능: Kotlin 컬렉션은 데이터를 추가, 삭제, 검색, 정렬하는 등의 작업을 효율적으로 수행하도록 도와줍니다.

2. 자바 컬렉션과 Kotlin 컬렉션의 차이점

  • 자바 컬렉션 활용: Kotlin은 자바의 컬렉션을 기본으로 사용하며, 자바와의 상호운용성을 높입니다.
  • 확장 함수: Kotlin에서는 자바 컬렉션에 last(), filter { }와 같은 편리한 확장 함수를 제공하여 코드를 간결하게 작성할 수 있습니다.

3. Kotlin 컬렉션의 종류와 예제 코드

3.1 List (리스트)

  • 정의: 순서가 있는 요소들의 집합으로 중복을 허용합니다.
  • 불변 리스트 (List)와 가변 리스트 (MutableList)로 구분됩니다.
  • 예제 코드:
    val immutableList = listOf(1, 2, 3)           // 불변 리스트
    val mutableList = mutableListOf(1, 2, 3)      // 가변 리스트
    mutableList.add(4)                            // 요소 추가 가능

3.2 Set (집합)

  • 정의: 순서가 없으며, 중복을 허용하지 않는 요소들의 집합입니다.
  • 불변 집합 (Set)과 가변 집합 (MutableSet)으로 구분됩니다.
  • 예제 코드:
    var immutableSet = setOf(1, 2, 3)             // 불변 집합
    val mutableSet = mutableSetOf(1, 2, 3)        // 가변 집합
    mutableSet.add(4)                             // 요소 추가 가능

3.3 Map (맵)

  • 정의: 키-값 쌍으로 구성된 컬렉션으로, 각 키는 고유하며 중복될 수 없습니다.
  • 불변 맵 (Map)과 가변 맵 (MutableMap)으로 구분됩니다.
  • 예제 코드:
    val immutableMap = mapOf(1 to "one", 2 to "two")   // 불변 맵
    val mutableMap = mutableMapOf(1 to "one", 2 to "two")
    mutableMap[3] = "three"                            // 요소 추가 가능

4. 불변 컬렉션과 가변 컬렉션 차이점

  • 불변 컬렉션: listOf, setOf, mapOf로 생성하며 수정할 수 없는 컬렉션입니다. 안전성과 예측 가능성을 높이기 위해 기본 컬렉션으로 사용됩니다.
  • 가변 컬렉션: mutableListOf, mutableSetOf, mutableMapOf로 생성하며, 데이터를 추가, 삭제, 변경할 수 있습니다.

5. Kotlin의 편리한 컬렉션 확장 함수

  • 확장 함수를 통해 컬렉션 작업을 더 간단하게 처리할 수 있습니다:
    • last(): 리스트의 마지막 요소를 반환
    • max() :
    • filter { }: 조건에 맞는 요소만 추출
    • map { }: 모든 요소에 함수를 적용해 변환
  • 예제 코드:
    val list = listOf(1, 2, 3, 4, 5)
    val evenNumbers = list.filter { it % 2 == 0 }   // 짝수만 추출
    val doubled = list.map { it * 2 }               // 모든 요소를 2배로 변환
    println(evenNumbers)    // [2, 4]
    println(doubled)        // [2, 4, 6, 8, 10]
    

6. Kotlin 컬렉션의 장점 요약

  • 자바 컬렉션 호환성: 자바의 컬렉션을 그대로 사용할 수 있어 상호운용성이 뛰어납니다.
  • 불변성을 지닌 컬렉션: 안정성과 예측 가능성을 높여 안전한 코드 작성에 도움을 줍니다.
  • 간결한 코드: 다양한 확장 함수를 통해 코드가 간단하고 직관적입니다.

약간 더 들어가보면?

6.1컬렉션 변환 함수

  • 컬렉션을 다른 타입으로 변환할 수 있습니다. toList(), toSet(), toMap() 등으로 타입 변환이 용이합니다.
  • 예제 코드:
    val list = setOf(1, 2, 3).toList()
    val set = listOf(1, 2, 2, 3).toSet() // 중복 제거
    

6.2 유용한 컬렉션 연산자

  • 연산 예제:
    // groupBy: 요소들을 그룹화
    val grouped = listOf(1, 2, 3, 4, 5).groupBy { it % 2 == 0 }
    
    // flatten: 중첩된 컬렉션을 단일 수준으로 평탄화
    val nested = listOf(listOf(1, 2), listOf(3, 4))
    val flat = nested.flatten() // [1, 2, 3, 4]

6.3 불변성과 가변성의 차이점

  • 불변 컬렉션은 데이터 수정을 허용하지 않지만, 참조 자체는 다른 컬렉션으로 교체 가능합니다.
    val numbers = listOf(1, 2, 3) //불변 리스트
    numbers.add(4)  // 컴파일 에러 발생
    
    // 참조 교체는 가능
    var numbers2 = listOf(1, 2, 3)
    numbers2 = listOf(1,2,3,4)  // 새로운 리스트로 교체 가능

6.4 컬렉션 성능 고려 사항

  • List: 인덱스 접근 O(1)
  • Set: 요소 탐색 O(1)
  • Map: 키 탐색 O(1)
  • 성능 예시:
    val arrayList = ArrayList<Int>()   // 랜덤 접근에 유리
    val linkedList = LinkedList<Int>() // 삽입/삭제에 유리

6.5 고급 컬렉션 기능

  • 연산 예제:
    // zip: 두 컬렉션 결합
    val numbers = listOf(1, 2, 3)
    val letters = listOf("A", "B", "C")
    val zipped = numbers.zip(letters) // [(1, "A"), (2, "B"), (3, "C")]
    
    // associate: 키-값 쌍으로 맵 생성
    val mapped = listOf("one", "two").associate { it to it.length } // {"one": 3, "two": 3}
    
    // windowed: 윈도우 크기로 분할
    val nums = listOf(1, 2, 3, 4)
    val windows = nums.windowed(size = 2) // [[1, 2], [2, 3], [3, 4]]
    

7. 컬렉션 활용 예시

  • 실제 예제:
    data class User(val name: String, val age: Int)
    
    val users = listOf(
        User("Alice", 25),
        User("Bob", 30),
        User("Charlie", 35)
    )
    
    val result = users
        .filter { it.age > 25 }
        .map { it.name }
        .sorted()
        .joinToString(", ")
    
    println(result) // "Bob, Charlie"
profile
응애 개발맨
post-custom-banner

0개의 댓글