< Dev-Kidult />

Java to Kotlin - 4. 코틀린 표준 라이브러리 본문

개발/Back-end

Java to Kotlin - 4. 코틀린 표준 라이브러리

개른이 2022. 12. 28. 16:23

Collections

코틀린과 자바와의 제일 큰 차이점이라면 코틀린에는 쓰기가능한 콜랙션이 따로 존재한다는점이다.

기존 자바에서는 var a = List.of(); 이렇게 선언되어있을 때 a변수에 add remove등이 다 가능했다면 코틀린에서는 add, remove같은 함수는 사용하지 못한다.(읽기전용)

 

그렇다면 코틀린에서는 어떻게 해야 할까. 코틀린의 collection은 아래와 같이 이루어져있다.

 

기존 자바와 다르게 Mutable이라고 붙어져 있는 인터페이스들이 존재한다.

해당 인터페이스들을 통해 데이터를 넣거나 빼거나 하는 등의 작업을 할 수 있다.

 

stream → iterable

자바에서는 리스트 연산을 위해서는 stream으로 변환을 한 뒤 해줘야 하는 불편함이 있다.(다시 리스트로 변환해줘야하는 거까지)

var a = List.of(1,2,3,4,5);
var b = a.stream().filter(i -> i > 3 ).toList();

 

하지만 코틀린에서는 Iterable 및 Array에 확장함수로 해당 기능들이 다 구현되어있어서 간단한다.

자바와 다르게 array에도 구현이 되어있기에 array를 꼭 collection으로 변환하여 작업할 필요가 사라진다.

val a = mutableListOf(1,2,3,4,5)
val b = a.filter { it > 3 }
val c = intArrayOf(1,2,3,4,5)
val d = c.filter { it > 3 }

sequence

자바에서 stream은 lazy evaluation을 사용하는 대표적인 연산방식이다.

그렇다면 코틀린에서의 iterable은 어떻게 동작하는가

val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)

println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)

각각의 연산에 대해서 전부 처리한 뒤 해당 iterable을 다음 체인에 넘겨주는 방식이다.

이러면 많은 값이 담겨있는 iterable의 경우에는 필요없는 연산이 비약적으로 늘어날 것이다.

 

그래서 코틀린에는 sequence라는 표준라이브러리가 따로 있다.

val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a Sequence
val wordsSequence = words.asSequence()

val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)

println("Lengths of first 4 words longer than 3 chars")
// terminal operation: obtaining the result as a List
println(lengthsSequence.toList())

위 동작방식을 보면 우리가 아는 자바의 stream과 같은 방식으로 처리함을 알 수 있다.
 

 

범위지정함수(Scope function)

코틀린 표준 라이브러리에는 개체의 컨텍스트내에서 특정작업을 실행 할 수 있도록 하는 함수가 있다.

let, run, with, apply, also 이렇게 다섯가지이며, 각각 아래 표와 같은 차이점을 보인다.

 
fun object reference return value
let it lambda result
run this lambda result
run - lambda result
with this lambda result
apply this Context object
also it Context object

 

let

let은 수신객체를 활용하여 무언가의 작업을 하고 마지막줄을 리턴할 때 쓴다.

하는일은 run과 with와 비슷하지만 수신객체에 접근자로 it을 사용한다.

추가로 nullable의 체크를 같이 하는 작업은 주로 let을 쓴다.

val str: String? = "Hello"   
val length = str?.let { 
    println("let() called on $it")        
    it.length
}

 

run

하는일은 let과 비슷하지만 수신객체 접근자로 this를 사용하는점과 수신객체를 안받고 사용가능하다는 점이 있다.

val hexNumberRegex = run {
    val digits = "0-9"
    val hexDigits = "A-Fa-f"
    val sign = "+-"

    Regex("[$sign]?[$digits$hexDigits]+")
}

for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
    println(match.value)
}

 

with

let, run과 하는일은 비슷하지만 수신객체를 인자로 받는 차이점이 있다.

보통 with를 쓰기보단 대신 run을 사용하는 경우가 많다.

val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
    "The first element is ${first()}," +
    " the last element is ${last()}"
}
println(firstAndLast)

 

apply

apply는 수신객체 내부 프로퍼티를 구성할 때 쓴다.

val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}

 

also

apply가 수신객체의 내부 프로퍼티만 셋팅하는데 쓴다면, also는 프로퍼티 셋팅뿐만 아니라 추가적인 작업(로깅, 유효성검사등…)을 한 후 객체를 반환할 때 쓴다.

val adam = Person("Adam").also {
    age = 32
    city = "London"        
    logging.info("create person, name: $name")
}
 
반응형
Comments