[Kotlin] 코틀린과 표준함수
클로저
: 람다식으로 표현된 내부 함수에서 외부 범위에 선언된 변수에 접근할 수 있는 개념
람다식 안에 있는 외부 변수는 값을 유지하기 위해 람다가 포획한 변수 !
fun main() {
val calc = Calc()
var result = 0 //외부변수
calc.addNum(2,3) { x, y -> result = x + y } //result의 결과가 함수 외부의 result에 반영됨
println(result) // 5
}
class Calc(){
fun addNum(a: Int, b: Int, add: (Int, Int) -> Unit){ //람다식에서는 반환값 x
add(a,b)
}
}
코틀린의 표준 라이브러리 함수
- let()
- apply()
- with()
- also()
- run()
let()
: T를 매개변수로 받아 block으로 넘기고 block의 결과값 R을 반환
//T : 사용요소, R : 반환요소
public inline fun<T, R> T.let(block: (T) -> R): R{ ... return block(this)}
fun main() {
val score : Int? = 32
//var score = null
fun checkScore(){
if(score != null) {
println("Score: $score")
}
}
fun checkScoreLet(){
score?.let{ println("Score: $it") } //score를 받아서 it으로 사용할 수 있음
val str = score.let { it.toString() }
println(str)
}
checkScore()
checkScoreLet()
}
- Let함수의 체이닝
fun main() {
var a = 1
val b = 2
a = a.let { it + 2 }.let { //a에다가 2 더한 결과값에 let을 바로 사용해서 {}안의 내용을 실행
println("a = $a")//3 출력 후
val i = it + b // 2를 더함
i //결과값이 a에 반영됨
}
println(a) //5
}
- 중첩 사용
var x = "Kotlin"
x.let { outer -> //it을 사용하지 않고 명시적 이름 사용
outer.let { inner ->
print("Inner is $inner and outer is $outer")
}
}
- 반환값은 바깥쪽의 람다식에만 적용
var x = "Kotlin"
x = x.let { outer -> // x 에 let안의 내용을 할당
outer.let { inner ->
print("Inner is $inner and outer is $outer")
"Inner String"//할당 x
}
"Outer String" //이것만 x에할당됨
}
- 안드로이드에서의 let 활용
let을 사용하지 않을 때
val padding = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics).toInt()
setPadding(padding,0,padding, 0)
let을 사용
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f,
resouces.displayMetrics).toInt().let{ //padding -> 해서 padding으로 사용할수도 있음
setPadding(it,0,it,0)//인자가 한개이기 때문에 it 사용
}
- null 검사
safeCall 과 같이 사용 !
val obj : String?
obj?.let { //null이 아닌 경우에만 수행
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
- else문이 포함된 문장 대체
val firstName : String?
firstName?.let { print("$it") } ?: print("0") //null이 아닌경우만 let 블록을 수행하기 때문에, null이라면 0을 출력
also()
: 함수를 호출하는 객체 T를 이어지는 block에 전달하고 객체 T 자체를 반환
public inline fun <T> T.also(block: (T) -> Unit) : T {block(this); return this }
var m = 1
m = m.also{ it + 3 } //블록 안의 코드 수행결과와 상관없이 원본 객체 m을 반환하기 때문에 1이 반환됨
println(m) //1
- let과 also
fun main(){
data class Person(var name: String, var skills : String){}
var person = Person("kildong", "Kotlin")
val a = person.let {
it.skills = "Android" //person객체의 skills가 Android로 변경됨
"success" //a에 success가 반영
}
println(person)// Person(name=kildong, skills=Java)
println("a: $a") // a: success
val b = person.also{
it.skills = "Java" //person객체의 skills가 Java로 변경됨
"success" //객체 person 자체를 반환하기 때문에 b에 success가 반영되지 않음
}
println(person)// Person(name="kildong", skills="Java")
println("b: $b") // Person(name="kildong", skills="Java")
}
- 안드로이드에서의 let과 also 활용
let과 also를 사용하지 않을 때
fun makeDir(path: String) : File {
val result = File(path)
result.mkdirs()
return result
}
let과 also를 사용할 때
//let은 File로 만들고 also를 통해 넘겨 디렉토리로 변경된다.
//이때 also에서는 결과값을 반환하지 않기 때문에, 최종적으로 디렉토리 변경까지 마친 결과가 let에서 반환된다.
fun makeDir(path : String) = path.let{File(it)}.also{it.mkdirs()}
👍 let()과 also()의 차이점 정리
⇒ let() : block 안의 내용을 적용시키고, 그 결과 반환
⇒ also() : block 안의 내용만 적용 시키고 결과 반환 x
apply()
: also()함수와 마찬가지로 호출하는 객체 T를 이어지는 block으로 전달하고 객체 자체인 this를 반환
확장 함수 형태로 처리됨 ! → it을 사용하지 않고 this로 사용 하지만 this는 생략 가능
public inline fun <T> T.apply(block: T.() -> Unit) : T { block(); return this }
data class Person(var name: String, var skills : String){}
var person = Person("kildong", "Kotlin")
//this는 person을 가리킴
person.apply{ this.skills = "Swift" }
println(person) //Person(name=kildong, skills=Kotlin)
val returnObj = person.apply{
name = "Sean" //this 없이 객체의 멤버에 여러번 접근
skills = "Java"
}
println(person) //Person(name=Sean, skills=Java)
println(returnObj) //Person(name=Sean, skills=Java)
👍 also()와 apply()의 차이점 정리
⇒ also() : it으로 받고 생략할 수 없음
⇒ apply() : this로 받고 생략 가능, 반복되는 객체의 변수를 다룰 때 주로 사용하는 듯 obj.name obj.grade 이런식으로 obj 하나하나 안쓰고 걍 name, grade로 바로 사용
- 안드로이드에서의 apply
apply를 사용하지 않았을 때
val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT)
param.gravity = Gravity.CENTER_HORIZONTAL
param.weight = 1f
param.topMargin = 100
param.bottomMargin = 100
apply를 사용할 때
val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
gravity = Gravity.CENTER_HORIZONTAL
weight = 1f
topMargin = 100
bottomMargin = 100
}
//fun makeDir(path : String) = path.let{File(it)}.also{it.mkdirs()}
File(path).apply { mkdirs() } //이렇게 간단하게 사용할 수 있음
⇒ let과 also를 사용했던 문장을 → apply하나만 사용해서 더 간단하게 만들 수 있음
run()
: 인자가 없는 익명함수처럼 동작하는 형태와 객체에서 호출하는 형태 두 가지로 사용
public inline fun <R> run(block: ()-> R) : R = return block()
public inline fun <T, R> T.run(block: T.() -> R): R = return block()
fun main() {
data class Person(var name: String, var skills : String)
var person = Person("kildong", "Kotlin")
val returnObj = person.apply{
name = "Sean"
skills = "Java"
"success" //reutrn 반영 안됨
}
println(person) //Person(name=Sean, skills=Java)
println("returnObj: $returnObj") // returnObj: Person(name=Sean, skills=Java)
val returnObj2 = person.run(){
name = "Dooly"
skills = "C#"
"success" //return 반영됨
}
println(person) //Person(name=Dooly, skills=C#)
println("returnObj2: $returnObj2") // returnObj2: success
}
👍 apply()와 run()의 차이점 정리
⇒ apply() : 리턴값 반영 x
⇒ run() : 리턴값 반영 o
with()
: 인자로 받는 객체를 이어지는 block의 receiver로 전달하며 결과값 반환
run() 함수와 기능이 거의 동일하다. run의 경우 receiver가 없지만, with()에서는 receiver로 전달 할 객체를 처리
public inline fun <T, R> with(receiver: T, block: T.() -> R) : R = receiver.block()
세이프 콜은 지원하지 않기 때문에 let과 같이 사용
supportActionBar?.let {
with(it) { //it = supportActionBar
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_clear_white)
}
}
let과 with 표현 병합 = run과 동일
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_clear_white)
}
fun main() {
data class Person(var name: String, var skills : String)
var person = Person("kildong", "Kotlin")
val result = with (person){
name = "Sean"
skills = "Java"
//"Hello"
}
println(person) //Person(name=Sean, skills=Java)
println("returnObj: $result") // result: kotlin.Unit -> 반환값이 없다면 Unit 반환, 반환값이 있다면(예제에선 Hello) Hello반환
}
use()
: 객체를 사용한 후 close() 등을 자동적으로 호출해서 닫아줌
public inline fun <T : Closeable?, R> T.use(block: (T) -> R) : R
or
public inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R) : R
ex)파일 사용
private fun readFirstLine() : String {
BufferedReader(Filereader("test.file")).use { return it.readLine() }
}
takeIf()와 takeUnless()의 활용
- takeIf() : 람다식이 true이면 객체 T를 반환하고 그렇지 않은 경우 null 반환
- takeUnless() : 람다식이 false이면 객체 T를 반환하고 그렇지 않은 경우 null반환
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
= if (predicate(this)) this else null
val input = "Kotlin"
val keyword = "in"
input.indexOf(keyword).takeIf { it >= 0 } ?: error("keyword not found")
input.indexOf(keyword).takeUnless { it < 0 } ?: error("keyword not found")
Comments