티스토리 뷰

람다 표현

  • 람다식은 항상 중괄호로 감싼다.
  • 파라미터 -> 몸체 형식으로 선언한다.
  • 인자는 형식 추론이 가능하면 생략 가능하다.
  • 바로 실행시 run을 사용한다.
fun main () {
    val f: () -> String = { "hello lambda" }
    println(f()) // hello lambda

    run { println("hello lambda") } // hello lambda
}

람다 사용 규칙

  • 함수의 맨마지막이 람다면 ()안에서 빼내어 사용 가능하다.
  • 인자가 하나라면 람다식 내부에서 it키워드로 대체해서 사용가능하다.
  • 인자가 하나이면서 인자 타입이 람다라면 ()를 생략 가능하다.
fun calculator(a: Int, b: Int, f: (a: Int, b:Int) -> Int) = f(a, b)
fun call(f: () -> Unit) = f()

fun main () {
    println(calculator(1, 2) { a, b -> a + b }) // 3
    call { println("call lambda function") } // call lambda function
    println(listOf(1, 2, 3, 4).maxBy { it }) // 4
}

Lambda Capturing

자바

자바에서 람다가 접근할 수 있는 변수는 세종류가 있다.

  • 지역변수
  • static 변수
  • 멤버 변수

이중 지역 변수만 final일때 접근이 가능하다.

변수를 저장하는 방식이 상이해서 나타난다.

변수 종류 저장되는 위치
지역변수 Stack영역
참조변수 Heap영역

특정 함수내에 람다식이 있었고 람다식 내에서 함수 내부에 있는 지역변수를 사용할 경우 문제가 발생할 수 있다.

Stack영역의 경우 함수의 사용이 완료되면 함수정보는 삭제된다.

함수 내부에 람다식이 함수가 끝난후에 실행될 경우 이미 Stack영역에는 함수에 대한 정보(지역변수)가 없어져있기 때문에 람다 내부에서 지역변수에 쓰기 연산을 하면 안된다.

그래서 람다는 지역 변수의 복사본을 만들어 접근하게 되는데 원본값과 복사한 값이 다르면 안되기 때문에 final지역변수만 접근 가능한 제약이 생겼다.

코틀린

코틀린의 경우 지역변수도 final제약없이 사용할 수 있다.

만약 지역변수가 final로 선언되어 있다면 지역 변수를 그래도 복사하여 람다 코드 내부에서 사용하게 된다.

final이 아니라면 특별한 래퍼 클래스로 감싸 final변수(Heap)에 담아 람다 코드 내에서 사용한다.

이로인해 코틀린에서는 지역변수에 구애받지 않고 읽기/쓰기가 가능하다.

member reference

  • 클래스 멤버 표현
class Person {
    val name = "park jin"
}

fun main() {
    val property = Person::name
    println(property.get(Person())) // park jin
}
  • 최상위 함수
fun printAll() {
    
}

fun main() {
    val f = ::printAll
    f()
}
  • 생성자
class Person

fun main() {
    val constructorPerson = ::Person
    val person:Person = constructorPerson()
}
  • bound member
class Person {
    val name = "jin"
}

fun main() {
    val person = Person()
    val getName = person::name
    println(getName()) // jin
}

수신 객체 지정 람다 - with, apply, let

let

호출하는 객체를 이어지는 블록의 인자로 넘기고, 블록의 결과값을 반환한다.

  • 지정된 값이 null 이 아닌 경우에 코드를 실행해야 하는 경우.
  • 단일 지역 변수의 범위를 제한 하는 경우.
fun nullSafeUpperCase(value: String?) = value?.let { it.toUpperCase() }

class Dao
fun getDao() = Dao()

fun main() {
    val name: String? = "park jin" // nullable
    println(nullSafeUpperCase(name)) // PARK JIN

    // DAO 변수 범위 제한
    getDao().let {
        // dao.insert()
        // dao.update()
        // ....
    }
}

apply

apply 함수를 호출하는 객체를 블록의 리시버로 전달하고, 객체 자체를 반환한다.

  • 객체의 초기화
data class Person(var name: String = "", var age: Int = 0)

fun main() {
    val person = Person().apply {
        name = "park jin"
        age = 31
    }

    println(person) // Person(name=park jin, age=31)
}

with

인자로 받는 객체를, 블록의 리시버로 전달하고, 블록의 결과값을 반환한다.

  • Non-nullable (Null 이 될수 없는) 수신 객체이고 결과가 필요하지 않은 경우에 with 를 사용한다.
class Person(var name: String)
class PersonDao { 
    fun save(p: Person): Boolean {
        // save person object
        println("save person :: $p")
        return true
    }
}

fun main() {
    val isSaved: Boolean = with(Person("park jin")) {
        PersonDao().save(this)
    }
}

run

run 함수는 이미 생성된 객체의 메서드나 필드를 연속적으로 호출할 때 사용한다.

  • 어떤 값을 계산할 필요가 있거나 여러개의 지역 변수의 범위를 제한 할때 사용.
  • 매개 변수로 전달된 명시적 수신객체 를 암시적 수신 객체로 변환 할때 사용.
val inserted: Boolean = run {
    // person 과 personDao 의 범위를 제한 합니다.
    val person: Person = getPerson()
    val personDao: PersonDao = getPersonDao()
    // 수행 결과를 반환 합니다.
    personDao.insert(person)
}
fun printAge(person: Person) = person.run {
    // person 을 수신객체로 변환하여 age 값을 사용합니다.
    print(age)
}

also

apply와 동일하나 코드 블럭 내부에서 프로퍼티 변경하지 못한다 ex) 데이터 유효성 검사

호출 체인

이건 좀더 공부 해봐야 할듯...

private fun insert(user: User) = SqlBuilder().apply {
  append("INSERT INTO user (email, name, age) VALUES ")
  append("(?", user.email)
  append(",?", user.name)
  append(",?)", user.age)
}.also {
  print("Executing SQL update: $it.")
}.run {
  jdbc.update(this) > 0
}

출처

...더보기

https://medium.com/@limgyumin/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%9D%98-apply-with-let-also-run-%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-4a517292df29

 

코틀린 의 apply, with, let, also, run 은 언제 사용하는가?

원문 : “Kotlin Scoping Functions apply vs. with, let, also, and run”

medium.com

https://jinn-blog.tistory.com/29

 

람다(Lambda)

람다 표현 람다식은 항상 중괄호로 감싼다. 파라미터 -> 몸체 형식으로 선언한다. 인자는 형식 추론이 가능하면 생략 가능하다. 바로 실행시 run을 사용한다. fun main () { val f: () -> String = { "hello lambd..

jinn-blog.tistory.com

https://black-jin0427.tistory.com/169

 

[Android, Kotlin] 코틀린의 run, with, apply, let, also 정리

안녕하세요. 블랙집입니다. 그동안 코틀린을 사용하면서 run, with, apply, let, also 를 사용했지만 내용을 한번 정리해 보면 좋을 것 같아 간략히 포스팅을 합니다. this run , with , and apply refer to the c..

black-jin0427.tistory.com

https://jepark-diary.tistory.com/69

 

코틀린 헷갈리는 함수 정리. let, apply, run, with

코틀린 헷갈리는 함수 정리. let, apply, run, with 1. let let 함수를 호출하는 객체를 블록의 인자로 넘기고, 블록의 결과값을 반환한다. 또한 널 처리(if (a != null) 를 대신할 수 있다. inline fun <t, r=""> T...</t,>

jepark-diary.tistory.com

https://feco.tistory.com/64

 

람다 표현식에서 참조하는 지역 변수가 꼭 final이어야 하는 이유

람다 캡처링(Capturing lambda) 람다 표현식에서는 익명 함수에서와 마찬가지로, 외부에서 정의된 변수(* 책에서는 자유변수free variable 라고 소개됨)를 활용할 수 있다. (이와 같은 액션을 람다 캡처링(capturi..

feco.tistory.com

 

'개발 언어 > 코틀린' 카테고리의 다른 글

코틀린 & Spring Boot 2.1  (0) 2019.06.27
코틀린(Kotlin) | 함수  (0) 2019.06.24
코틀린(Kotlin) | 프로퍼티 위임  (0) 2019.06.24
코틀린(Kotlin) | 클래스 위임  (0) 2019.06.24
코틀린(Kotlin) | 클래스 - Enum  (0) 2019.06.24
댓글