-
kotlin) 추가적으로 알아두어야 할 코틀린 특성kotlin 2024. 7. 13. 17:04
코틀린의 이모 저모
1. Type Alias 와 as import
개념 : 긴 이름의 클래스 혹은 함수 타입이 있을때 축약하거나 더 좋은 이름을 쓰고 싶을때 사용
Type Alias
//기존 함수 fun filterFruits(fruits: List<Fruit>, filter: (Fruit) -> Boolean): List<Fruit> { } //typealias 를 통해 미리 type 을 정해서 적용 typealias FruitFilter = (Fruit) -> Boolean fun filterFruits(fruits: List<Fruit>, filter: FruitFilter): List<Fruit> { } data class UltraUperGuardianTribe( val name : String ) //이름이 긴 클래스를 컬렉션에 사용할 때도 간단하게 줄일 수 있다. typealias USGTMap = Map<String, UltraUperGuardianTribe>
as import
//사용하려는 패키지의 이름을 변경할 수 있다. import com.lannstart.lec19.a.printHellowWorld as printHelloWorldA import com.lannstart.lec19.b.printHellowWorld as printHelloWorldB
2. 구조분해와 componentN 함수
구조분해
data class Person( val name: String, val age: Int ) fun main() { var person = Person("JS",100) val (name, age) = person //name, age 변수를 생성 // val name = person.component1() //name 를 각각 component 를 통해 가져옮 // val age = person.component2() //age 순서를 반드시 지켜야함 println("이름 : $name, 나이 : $age") }
componentN
class Person( private val name: String, private val age: Int ){ //연산자 이기 때문에 operator 함수를 사용해야함 operator fun component1(): String { return this.name } operator fun component2(): Int { return this.age } } fun main() { var person = Person("JS",100) val (name, age) = person println("이름 : $name, 나이 : $age") val map = mapOf(1 to "A", 2 to "B") for((key, value) in map.entries) { //(key,value) 역시 구조분해 문법 } }
3. Jump 와 Label
- return : 기본적으로 가장 가까운 encloseing function 또는 익명 함수로 값이 반환된다.
- break : 가장 가까운 루프가 제거된다.
- foreach 에서는 사용할 수 없다.
- 사용하고 싶다면 run 으로 감싼 함수에서 return@run 을 사용
- continue : 가장 가까운 루프를 다음 step 으로 보낸다.
- foreach 에서는 사용할 수 없다.
- 사용하고 싶다면 run 으로 감싼 함수에서 return@forEach 을 사용
Jump
fun main() { val numbers = listOf(1,2,3) numbers.map { number -> number + 1} .forEach { number -> println(number) } for( number in numbers){ if (number == 2) { continue //사용 가능 break //사용 가능 } } numbers.forEach { number -> if (number == 2){ continue //사용 불가능 break //사용 불가능 } } run{ //run 으로 한번 감싸야한다. numbers.forEach { number -> if (number == 2){ return@run //break 와 동일 return@forEach //continue 와 동일 } } } }
차라리 foreach 보다 for 문을 최대한 사용하는것을 추천
Label
fun main() { abc@ for(i in 1..100){ //"abc" 라벨을 생성하고 for(j in 1..100){ if(j == 2){ break@abc //"abc" 라벨 위치의 for 문을 종료 함 } println("${i} ${j}") } } }
4. TakeIf 와 TakeUnless
fun getNumberOrNull(): Int? { return if(number <= 0){ null } else { number } } fun getStringOrNull2(): Int? { return number.takeIf{it > 0} //주어진 조건을 만족하면 그 값이, 그렇지 않으면 null 이 반환 } fun getStringOrNull3(): Int? { return number.takeUnless{it <= 0} //주어진 조건을 만족하지 않으면 그 값이, 그렇지 않으면 null 이 반환 }
코틀린의 scope function
1. scope function 이란 무엇인가?
- scope : 영역
- function : 함수
- scope function : 일시적인 영역을 형성하는 함수
- 람다를 사용해 일시적인 영역을 만들고
- 코드를 더 간결하게 만들거나, method chaning 에 활용하는 함수를 scope function 이라고 합니다.
let
public inline fun <T,R> T.let(block: (T) -> R): R { //let : 확장함수. 람다를 받아, 람다 결과를 반환 return block(this) }
fun printPerson(person: Person?){ // if(person != null){ // println(person.name) // println(person.age) // } //위 코드를 아래 코드로 변경 person?.let{ //Safe.Call (?.) 을 사용 : person 이 null 이 아닐때 let 을 호출 println(it.name) //람다 {} 를 사용하며 람다 안에서 it 을 통해 person 에 접근 println(it.age) } }
2. scope function 의 분류
- let, run : 람다의 결과
- also, apply : 객체 그 자체
- with : this를 사용해 접근하고, this는 생략 가능하다.
val value1 = person.let{ it.age //let : 람다의 결과가 최종적으로 반영 } val value2 = person.run{ this.age //run : 람다의 결과가 최종적으로 반영 } val value3 = person.also{ it.age //also : 결과와는 무관하게 객체 그 자체, person 그 자체가 최종적으로 리턴 } val value4 = person.apply{ this.age //apply : 결과와는 무관하게 객체 그 자체, person 그 자체가 최종적으로 리턴 }
- this : 생략이 가능한 대신, 다른 이름을 붙일 수 없다.
- 파라미터 자체에서 일반 함수를 받는다. 그렇기에 일반 함수는 파라미터를 받아서 파라미터를 함수 내부에서 호출
- it : 생략이 불가능한 대신, 다른 이름을 붙일 수 있다.
- 확장함수를 받는다. 확장함수는 본인 자신을 this로 호출하고, 생략할 수 있다.
val value1 = person.let { p -> //person 의 다른이름 "p" 를 사용할 수 있다. p.age } val value2 = person.run { //person 의 이름 그대로 혹은 묵음,this 로 사용해야 한다. age }
3. 언제 어떤 scope function 을 사용해야 할까?
- 하나 이상의 함수를 call chain 결과로 호출할 때
- non-null 값에 대해서만 code block 을 실행시킬 때
- 일회성으로 제한된 영역에 지역 변수를 만들 때
4. scope function 과 가독성
scope function 을 사용한 코드가 그렇지 않은 코드보다 가독성이 좋은 코드일까?
// 1번 코드 if (person != null && person.isAdult){ view.showPerson(person) } else { view.showError() } // 2번 코드 person?.takeIf { it.isAult } ?.let(view::showPerson) ?: view.showError()
- 구현 2는 숙련된 코틀린 개발자만 더 알아보기 쉽다. 어쩌면 숙련된 코틀린 개발자도 잘 이해하지 못할 수 있다.
- 구현 1의 디버깅이 쉽다.
- 구현이 1이 수정도 더 쉽다.
사용 빈도가 적은 관용구는 코드를 더 복잡하게 만들고 이런 관용구들을 한 문장 내에서 조합해 사용하면 복잡성이 훨씬 증가한다.
'kotlin' 카테고리의 다른 글
kotlin) 복잡한 함수형 크로그래밍 (0) 2024.07.20 kotlin) Kotlin 의 지연과 위임 (0) 2024.07.18 kotlin) 코틀린에서의 FP (0) 2024.07.09 kotlin) 코틀린에서의 OOP (0) 2024.07.06 kotlin) 코틀린에서 변수와 타입, 연산자를 다루는 방법 (0) 2024.07.04