인프런/자바 개발자를 위한 코틀린 입문

12) 코틀린의 이모저모

backend dev 2025. 1. 11.

1. Type Alias와 as import 

 

Type Alias

긴 이름의 클래스 또는 함수 타입이 있을때 별칭으로 사용할 수 있다.

typealias FruitFilter = (Fruit) -> Boolean // 별칭설정

private fun filterFruits(
    fruits: List<Fruit>,
    funcName: FruitFilter // 별칭으로 대체가능
): List<Fruit> {

    return fruits
        .filter(funcName)

}

data class suuuuuuuuuuuuuuuperdata(
    val name:String
)

typealias superMap = Map<String,suuuuuuuuuuuuuuuperdata>

fun superMapOut(): superMap? {
    return null
}

 

 

다른 패키지의 같은 이름인 함수를 동시에 가져오고싶다면

package lannstark.lec01

fun hello() {
    println("hello lec01")
}


package lannstark.lec02

fun hello() {
    println("hello lec02")
}

 

 

as import : 어떤 클래스나 함수를 임포트 할 때 이름을 바꾸는 기능

import lannstark.lec01.hello as helloFromLec01
import lannstark.lec02.hello as helloFromLec02

fun main() {
    helloFromLec01()
    helloFromLec02()
}

as 를 통해 각 함수의 이름을 다르게하여 가져온다.

 

as import 기능을 안쓴다면

package lannstark.lec03

import lannstark.lec02.hello

fun main() {
    hello() // 먼저 import 해온 함수는 이름만으로 사용가능하다.
    lannstark.lec01.hello() // 추후에 가져온 함수는 디렉토리 위치를 이용해야한다.
    
    lannstark.lec02.hello() // 물론 먼저 가져온 함수도 디렉토리 위치를 이용해서 사용가능하다.
}

 


 

2. 구조분해와 componentN 함수

 

구조분해 : 복합적인 값을 분해하여 여러 변수를 한 번에 초기화하는 것

fun main() {
    val person = Person("gil", 10)
    val (name,age) = person // 구조분해는 로컬변수나 로컬값으로만 가능하다.

    val person2 = Person2("gil", 10)
    val (name2,age2) = person2
    
    val name3= person.component1() // componentN 함수를 직접 사용할수도 있다.
    val age3= person.component2()
}

data class Person(
    val name:String,
    val age:Int
)

//data class가 아닐때 구조분해를 사용하려면 componentN()를 수동 추가해줘야한다.
class Person2(
    val name:String,
    val age:Int
){
    operator fun component1() = name
    operator fun component2() = age
}

 

Data ClasscomponentN 함수도 자동으로 만들어준다

 

 

Data Class가 아닌데 구조분해를 사용하고 싶다면, componentN 함수를 직접 구현해주면된다.

 

val map = mapOf(1 to "A",2 to "B")
for ((key, value) in map.entries) {  // (key,value) 또한 구조분해이다. entry를 분해한것
    
}

 

val (age,name) = person // 받는 순서를 주의해야한다. 컴포넌트 순서대로 나오기 때문이다.

 

 

operator fun component1() = name
operator fun component2() = age

operator 지시어는 연산자 오버로딩(operator overloading)을 허용하기 위해 사용됩

 

operator를 이용하여 기본적으로 정의된 연산자를 오버로딩 할 수 있다.

 

(a,b) 같은 구조분해선언도 연산자이기때문에 operator를 붙여줘야한다.

 

+ 연산자 오버로딩 예시)

data class Vector(val x: Int, val y: Int) {
    operator fun plus(other: Vector): Vector {
        return Vector(x + other.x, y + other.y)
    }
}

fun main() {
    val v1 = Vector(3, 4)
    val v2 = Vector(1, 2)
    val result = v1 + v2 // operator fun plus 호출
    println(result) // Vector(x=4, y=6)
}

 

 

 

 

Kotlin에서 람다식 안에서 매개변수를 ()로 감싸면 구조 분해(destructuring)로 인식

 

일반코드)

fun printPerson(person: Person?) {
    person?.let{ p ->
        println(p.name)
        println(p.age)

    }
}

구조분해 사용)

fun printPerson(person: Person?) {
    person?.let{ (name,age) ->
        println(name)
        println(age)
    }
}

 

 


3. Jump와 Label

 

 

- return : 기본적으로 가장 가까운 enclosing function[감싸져있는 함수] 또는 익명함수로 값이 반환된다

=> 가까운 함수가 종료되면서 값을 반환해준다는 뜻

 

- break : 가장 가까운 루프가 제거된다

 

- continue : 가장 가까운 루프를 다음 step으로 보낸다

 

 

코틀린에서 for문 및 while 문에서 break, continue 기능은 동일하다.

 

자바,코틀린 둘다 forEach에서 continue, break를 사용할 수 없다.

   val numberList = listOf(1, 2, 3)
    numberList.map { number -> number + 1 }
        .forEach {
            number ->
            print(number+1)
            println(number)
//            break 사용할 수 없다.
//            continue 사용할 수 없다.
        }

 

List<Integer> numbers = List.of(1, 2, 3);

numbers.stream()
        .forEach((number) ->{
            if (number == 3) {
                break; / /컴파일 에러
            }
        });

 

코틀린을 이용해서 forEach에서 continue, break를 사용하려면

run{
    numberList.forEach {
            number ->
        if(number==3){
            return@run // break 기능
        }
        if(number ==4){
            return@forEach // continue 기능
        }
        print(number+1)
        println(number)
    }
}

run이라는 중괄호에서 다음과 같이 사용하면된다. -> 밑에서 설명할 Label 기능을 이용해서 구현된것이다. 

run이라는 라벨을 return해서 foreach 전체를 끝내는 return기능을 구현한거고

foreach라는 라벨을 return해서 continue 기능을 구현한것이다.

[ 코틀린에서는 람다의 이름(혹은 함수 이름)을 암묵적으로 라벨로 사용할 수 있습니다.]

 

하지만 break, continue를 사용할 때엔 가급적 익숙한 for문 사용하자.

 

 

 

Label

코틀린에는 라벨이라는 기능이 있다

 

특정 expression에 라벨이름@붙여 하나의 라벨로 간주하고 break, continue, return 등을 사용하는 기능

 

abc@for (i in 1..100) {
    for (j in 100 downTo 1 step 2) {
        for (k in 50..500) {
            if (j == 2) {
                break@abc; // 라벨이 지정된 반복문을 탈출하게 된다. 
            }
        }
    }
}

 

라벨을 사용한 Jump는 사용하지 않는 것을 강력 추천한다.

 

 


 

4. TakeIf와 TakeUnless

 

Kotlin에서는 method chaning을 위한 특이한 함수를 제공한다.

 

takeIf

주어진 조건을 만족하면 그 값이, 그렇지 않으면 null이 반환된다

    fun getNumeberOrNull(number: Int): Int? {
//        return number.takeIf { it > 0 } 람다의 파라미터가 하나이고 컴파일러가 타입을 추론할 수 있는 경우 it 을 사용할 수 있다.
//        return number.takeIf { number > 0 } number 람다 외부의 변수이고, 코틀린은 람다 외부변수를 받을 수 있다.
        return number.takeIf { value -> value > 0 } // 일반적인 람다식
        // 위의 3가지 다 정상동작한다.
    }

위에서는 number가 0보다 크다면 number를 반환

아니라면 null을 반환해준다.

takeUnless

takeIf 와 반대로 동작한다.

주어진 조건을 만족하면 null , 그렇지 않으면 그 값이 반환된다.

return number.takeUnless { value -> value > 0 } 

takeUnless를 사용했다면 value가 0보다 크다면 null 아니라면 number를 반환한다.

댓글