ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5장 - 람다로 프로그래밍
    Kotlin/코틀린인액션요약 2022. 9. 13. 00:01
    728x90

    5장에서 다루는 내용

    - 람다 식과 멤버 참조

    - 함수형 스타일로 컬렉션 다루기

    - 시퀀스 : 지연 컬렉션 연산

    - 자바 함수형 인터페이스를 코틀린에서 사용

    -  수신 객체 지정 람다 사용

     

     

    자바 8을 사용했다면 람다식에 대해 어느 정도 알고 있을 거라 예상합니다.

     

    무명 클래스/ 익명클래스를 통해 인터페이스를 구현하는 방법은 불필요한 코드가 생기게 됩니다.

     

    이를 람다식을 활용하여 간결한 코드로 변환할 수 있지만 메서드가 하나뿐인 functional interface에서만 활용할 수 있습니다.

     

    코틀린에서는 이를 SAM 인터페이스라고도 부릅니다. (Single Abstract Method)

     

     

    만약 사람의 이름과 나이를 저장하는 Person 클래스에서 연장자를 찾고 싶다면 어떻게 해야 할까요?

    람다를 활용해본 적 없는 개발자라면 루프를 써서 직접 검색을 구현할 것입니다.

    하지만 루프 내부에 로직을 작성해야 하고 실수할 여지가 발생하게 됩니다.

     

    하지만 람다를 사용해 컬렉션을 검색하면 훨씬 간단해집니다.

    val people = listOf(Person("Alice",29), Person("Bob", 31))
    println(people.maxBy {it.age})

     

     

    람다식의 문법

    val sum = {x: Int, y: Int -> x + y}
    println(sum(1,2))

    -> 화살표로 파라미터와 본문을 구분합니다.

    x, y를 인자로 받고 x+y를 반환하는 구문입니다.

     

     

    위의 예제에서 코틀린이 제공한 기능들을 제공하고 정식으로 람다를 작성하면 다음과 같습니다.

    people.maxBy({p: Person -> p.age})

     

    만약 맨 뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼 수 있습니다.

    people.maxBy(){p: Person -> p.age}

     

    타입을 제거해도 컴파일러가 추론

    people.maxBy{p -> p.age}

     

    람다의 파라미터가 하나뿐이라면 it을 사용하면 컴파일러가 추론

    people.maxBy {it.age}

    하지만 람다가 중첩되는 경우 it을 사용하면 파악하기 힘들 수 있어 이런 상황에서는 명시적으로 파라미터를 선언하는 것이 좋습니다.

     

    멤버 참조 = 자바의 메서드 참조

    people.maxBy(Person::age)

     

    코틀린과 자바의 람다 차이

    코틀린에서는 파이널 변수가 아니어도 접근할 수 있습니다.

    또한 그 변수를 변경할 수도 있습니다.

     

    컬렉션 함수형 API

    map,filter 등 컬렉션 연산을 활용할 때 사용하는 함수들이 제공됩니다.

     

    fun main() {
        val list = listOf(1,2,3,4);
        println(list.filter{it % 2 == 0})
        //결과 : [2, 4]
    
        println(list.map{it * it})
        //결과 : [1, 4, 9, 16]
    }

     

    all, any, count, find, groupby,flatMap 등의 함수 제공

     

    지연 계산 컬렉션 연산

    자바 스트림과 달리 컬렉션 함수를 사용하면 결과 컬렉션을 즉시 생성합니다.

    하지만 시퀀스를 사용하면 중간 임시 컬렉션을 사용하지 않고 결과 컬렉션을 생성합니다.

     

    시퀀스 연산은 스트림처럼 중간연산과 최종 연산을 가집니다.

    println(list.asSequence()
            .map{it*it}
            .filter{it % 2 == 0}
            .toList())

    큰 컬렉션에 대해 연산을 연쇄시킬 때는 시퀀스를 사용하는 것을 규칙으로 삼아야 합니다.

    이유 : 중간 컬렉션을 재배열하는 비용이 커지기 때문

     

    중간 연산은 최종 연산이 수행되기 전까지 수행되지 않는 특징이 있습니다.

     

    스트림과 굉장히 유사한테 시퀀스라는 개념을 제공하는 이유는?

    안드로이드에서는 예전 자바 버전을 사용하는 경우 스트림이 없어서 제공합니다.

    자바 8을 사용하면 스트림을 병렬적으로 실행하는 기능을 사용할 수 있는 장점이 있습니다.

    따라서 상황에 따라 적절한 쪽을 선택해서 사용해야 합니다.

     

     

    수신 객체 지정 : with와 apply

    자바의 람다에는 없는 코트린 람다의 독특한 기능입니다.

     

    with 함수

    어떤 객체의 이름을 반복하지 않고 그 객체에 대해 다양한 연산을 수행할 수 있게 합니다.

     

    알파벳 만들기

    fun alphabet() : String{
        val result = StringBuffer()
        for (letter in 'A'..'Z') {
            result.append(letter)
        }
        result.append("\nNow I know the alphabet!")
        return result.toString()
    }
    
    fun main() {
        println(alphabet())
    }

    result를 계속 반복하여 사용하는 모습을 볼 수 있습니다.

     

    with를 사용한 알파벳 만들기

    fun alphabet() : String{
        val stringBuilder = StringBuilder()
        return with(stringBuilder){ //메서드를 호출하는 수신 객체 지정
            for (letter in 'A'..'Z') {
                this.append(letter) //this를 명시해 호출하는 수신 객체의 메서드를 호출
            }
            append("\nNow I know the alphabet!") //this를 생략하고 메서드 호출
            this.toString()     //반환
        }
        
    }
    
    fun main() {
        println(alphabet())
    }

     

    with와 식을 본문으로 하는 함수를 활용해 알파벳 만들기

    fun alphabet() =
        with(StringBuilder()) { //메서드를 호출하는 수신 객체 지정
            for (letter in 'A'..'Z') {
                append(letter) //this를 명시해 호출하는 수신 객체의 메서드를 호출
            }
            append("\nNow I know the alphabet!") //this를 생략하고 메서드 호출
            toString()     //반환
        }
    
    
    fun main() {
        println(alphabet())
    }

     

    apply 함수

    with와 유사하지만 apply 항상 자신에게 전달된 객체를 반환합니다.

     

    apply를 사용해서 알파벳 만들기

    fun alphabet() =
        StringBuilder().apply{
            for(letter in 'A'..'Z'){
                append(letter)
            }
            append("\n finish")
        }.toString()
    
    
    fun main() {
        println(alphabet())
    }

    댓글

Designed by Tistory.