Develop/kotlin

Kotlin (꿀)Tips!

에디개발자 2022. 4. 18. 07:00
반응형

Kotlin은 이미 많은 곳에서 사용하여 기본적인 문법에 대해선 다들 아실 것이라 생각합니다. :) 이미 많은 곳에서 Kotlin을 도입했거나 도입중 일 것입니다. 그리하여 이번 글에서는 Kotlin은 Tip의 관한 내용을 정리해보려합니다.  이 글은 Kotlin 홈페이지에서 Kotlin tips 의 내용을 정리한 글입니다. 좀 더 자세한 내용을 원하신다면 링크를 참조해주세요.

 

1. Timing Code

메서드 혹은 로직의 실행시간을 측정하려면 기존에는 System 시간을 가져와서 빼거나 StopWatch를 이용해서 start하고 stop하는 불편함을 감수했었습니다. 하지만 kotlin에서 지원하는 measureTimedValue 를 사용한다면 쉽게 실행시간과 반환 값을 구할 수 있습니다.

@OptIn(ExperimentalTime::class)  // 필수
@Test
fun `times`() {
    val (value, time) = measureTimedValue { longOperation() }  // use
    println("It took $time to caculate $value.")
}

fun longOperation(): String {
    repeat(20) {
        print(".")
    }
    return "DONE"
}

// response
....................It took 4.713ms to caculate DONE.

 

비슷한 메서드로 아래와 같이 지원합니다. 메서드의 이름만 봐도 어떤 결과값을 반환할지 알 수 있네요. 

 

2. Improving loop

Kotlin의 Loop를 개선하는 방법을 알아보겠습니다. 아래와 같은 Loop가 있습니다.

val fruits = listOf("Apple", "Banana", "Cherry", "Durian")

@Test
fun `improving loop`() {
    for (index in 0 .. fruits.size -1) {
        val fruit = fruits[index]
        println("$index: $fruit")
    }
}

// print
0: Apple
1: Banana
2: Cherry
3: Durian
덤으로 체크하고 넘어가자
0..5 0 <= .. <= 5
0 until 5 0 <= .. < 5

 

개선해보자

2-1. lastIndex

for (index in 0 .. fruits.lastIndex) {
    val fruit = fruits[index]
    println("$index: $fruit")
}

// code 
public val <T> List<T>.lastIndex: Int
    get() = this.size - 1

 

2-2. indices

for (index in fruits.indices) {
    val fruit = fruits[index]
    println("$index: $fruit")
}

// code
public val Collection<*>.indices: IntRange
    get() = 0..size - 1

 

2-3. withIndex, forEachIndexed

for ((index, fruit) in fruits.withIndex()) {
    println("$index: $fruit")
}

fruits.forEachIndexed { index, fruit ->
    println("$index: $fruit")
}

 

3. Strings

아래의 객체로 예제코드를 작성해보겠습니다.

data class Cat(val name: String, val sound: String, val photo: String)
val cats = listOf(
    Cat("Momo", "meow", "momo_dancing.jpg"),
    Cat("", "purr", "cat_sleeping.jpg"),
    Cat("Bella", "MEOW", "bella_hunting.jpg"),
)

 

3-1. removePrefix, removeSuffix, removeSurrounding

cats.map { it.photo.removeSuffix(".jpg") }
// [momo_dancing, cat_sleeping, bella_hunting]

"__MEOW__".removeSurrounding("__")
// MEOW

 

3-2. ifEmpty, ifBlank

cats.map { if(it.name.isBlank()) "the Cat" else it.name }

cats.map { it.name.ifBlank { "the Cat" } }  // Good!
// [Momo, the Cat, Bella]

 

3-3. equals(ignoreCase = ... )

// cat first sound: meow
// cat last sound: MEOW
cats.first().sound.lowercase() == cats.last().sound.lowercase()
// true

cats.first().sound.equals(cats.last().sound , ignoreCase = true)
// true

 

4. Doing more with the Elvis operator

Elvis operator: ?:

특정 메서드에서 반환 된 값이 null 이라면 default값을 주고 싶은 경우가 있을 것 입니다. 이런 경우는 간단하게 엘비스 연산자를 이용해 풀 수 있습니다.

fun getName(): String? {
    return null
}

val greeting = getName() ?: "undefined"  // log를 남기고 싶다면?

 

하지만 null을 반환했으니 log를 남기고 싶다면 아래와 같이 할 수 있습니다.

val greeting = getName() ?: run { 
    println("Oops, name was not set.")
    "undefined"
}

 

 

5. Kotlin Collections

아래의 데이터로 예시를 작성해보겠습니다.

data class Fruit(val name: String, val sugar: Int)

val fruits = listOf(
    Fruit("banana", 12),
    Fruit("apple", 10),
    Fruit("orange", 9),
    Fruit("pineapple", 10),
    Fruit("peach", 8),
    Fruit("lemon", 2),
    Fruit("mango", 13),
).sortedBy(Fruit::sugar)

 

5-1. take, takeFirst, drop, dropLast

fruits.take(2)
// [Fruit(name=lemon, sugar=2), Fruit(name=peach, sugar=8)]
// 앞의 2개

fruits.takeLast(3)
// [Fruit(name=pineapple, sugar=10), Fruit(name=banana, sugar=12), Fruit(name=mango, sugar=13)]
// 뒤의 3개

fruits.drop(2)
// [Fruit(name=orange, sugar=9), Fruit(name=apple, sugar=10), Fruit(name=pineapple, sugar=10), Fruit(name=banana, sugar=12), Fruit(name=mango, sugar=13)]
// 앞의 2개 삭제

fruits.dropLast(3)
// [Fruit(name=lemon, sugar=2), Fruit(name=peach, sugar=8), Fruit(name=orange, sugar=9), Fruit(name=apple, sugar=10)]
// 뒤의 3개 삭제

결과값으로 확인할 수 있듯이 기존의 리스트가 아닌 새로운 리스트를 반환한다.

 

5-2. Partition

val (sweet, superSweet) = fruits.partition { it.sugar < 10 }
// sweet: [Fruit(name=lemon, sugar=2), Fruit(name=peach, sugar=8), Fruit(name=orange, sugar=9)]
// superSweet: [Fruit(name=apple, sugar=10), Fruit(name=pineapple, sugar=10), Fruit(name=banana, sugar=12), Fruit(name=mango, sugar=13)]

// sweet: 10보다 작은 객체들
// superSweet: 10보다 크거나 같은 객체들

 

5-3. JoinToString

fruits.reversed().joinToString(
    separator = " + ",
    prefix = "GoodKotlin = [",
    postfix = "]",
    limit = 3,
    truncated = "MORE.."
) { it.name }

// GoodKotlin = [mango + banana + pineapple + MORE..]

 

같이 Kotlin 멋지게 써봐요. :)

반응형