Language/Kotlin

[KotlinInAction] 5장-람다로 프로그래밍(1) 람다 식과 멤버 참조

개랭갱깽스타 2021. 11. 24. 07:58

PREVIEW) 5장 - 람다로 프로그래밍

1. 컬렉션을 처리하는 패턴을 표준 라이브러리 함수에 람다를 넘기는 방식으로 대체하는 예제들

2. 자바 라이브러리와 람다를 함께 사용하는 방법

  - 자바에서 처음부터 람다를 고려하지 않고 만든 라이브러리라도 람다를 활용하게 만들 수 있다!

3. 수신 객체 지정 람다(lambda with receiver): 람다 선언을 둘러싸고 있는 환경과는 다른 상황에서 본문 실행

 

위 내용을 배우기 전에, 기본 다지기로! 람다로 프로그래밍을 하기 위한 개념을 먼저 집고 넘어가도록 하겠습니다. 꼬고!

 

0. 시작

람다(lambda)

다른 함수에 넘길 수 있는 작은 코드 조각

use case) 컬렉션 처리 

 

1.  람다 소개: 코드 블록을 함수 인자로 넣기

람다가 태어나기 전...

이벤트가 발생했을 때 핸들러 실행? 데이터 구조의 모든 원소에 이 연산을 적용? = 무명클래스 사용

클래스 선언 > 그  클래스의 인스턴스를 함수에 넘김

불편해ㅠㅠ

함수를 값처럼 다룬다.(함수를 직접 다른 함수에 전달) = 함수형 프로그래밍

Java vs Kotlin

Java

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                /* 클릭시 수행*/
            }
        });

Kotlin

val button = findViewById<Button>(R.id.button)
button.setOnClickListener { /* 클릭시 수행할 동작 */ }

 

2. 람다와 컬렉션

왜? 컬렉션에서 많이 사용할까?

컬렉션을 다룰 떄 수행하는 대부분의 작업은... 일반적인 패턴이 많다! (컬렉션 내 순회 등)

- 일반적인 패턴이라면, 라이브러리 안에서 제공해야 한다. > 이걸 처리하기에 람다가 편리하다!

Java8 이전에는 람다가 존재하지 않았기 때문에 개발자들이 직접 코드를 작성하곤 했다. > 코드의 중복... 노가다...

진짜 편한가?

data class Person(val name: String, val age: Int)

val people = listOf(Person("sybang", 20), Person("mjkim", 21), Person("khji", 26))

사람들로 이뤄진 리스트가 있을 때, 가장 연장자를 찾고 싶다!

Person(name=khji, age=26)

 

Legacy. 컬렉션을 직접 검색

fun findTheOldest(people: List<Person>) {
    var maxAge = 0
    var theOldest: Person? = null

    for(person in people) {
        if(person.age > maxAge) {
            maxAge = person.age
            theOldest = person
        }
    }

    print(theOldest)
}
    @Test
    fun ch_5_1_1_use_for_loop() {
        val people = listOf(Person("sybang", 20), Person("mjkim", 21), Person("khji", 26))
        findTheOldest(people)
    }

Lately.

1) 람다를 사용해 컬렉션 검색 maxBy

가장 큰 원소를 찾기위해 비교에 사용할 값을 돌려주는 함수를 인자로 받는다.

    @Test
    fun ch_5_1_2_use_lambda_maxBy() {
        val people = listOf(Person("sybang", 20), Person("mjkim", 21), Person("khji", 26))
        println(people.maxBy { it.age })
    }

 

2) 멤버 참조를 사용해 컬렉션 검색 ::

함수나 프로퍼티를 반환하는 역할을 수행하는 람다를 멤버 참조로!

    @Test
    fun ch_5_1_3_use_member_reference() {
        val people = listOf(Person("sybang", 20), Person("mjkim", 21), Person("khji", 26))
        println(people.maxBy (Person::age))
    }

 

3. 람다식의 문법

문법

{ x: Int, y: Int -> x + y }

변수에 저장

직접 호출

run

 

    @Test
    fun ch_5_2_1_use_lambda() {
        val people = listOf(Person("sybang", 20), Person("mjkim", 21), Person("khji", 26))
        println(people.maxBy({ p:Person -> p.age}))
    }

ㅠㅠㅠㅠ

구분자가 너무 많고, 컴파일러가 유추할 수 있는데 인자타입을 굳이 적어 주었다. 인자가 오직 1개일 때, 굳이 인자에 이름을 붙이지 않아도 된다!

 

맨 뒤에 있는 인자가 람다식이면, 그 람다를 괄호 밖으로 뺼 수 있다.

인자가 1개일 때) p. 203

인자가 여러개일 때) 람다를 밖으로 뺀다 / 람다를 괄호 안에 유지(얘가 더 보기 쉬움)

- 인자 목록의 맨 마지막 람다만 밖으로 뺼 수 있다. (추천 ㄴㄴ)

 

람다 파라미터의 타입도 추론할 수 있다. p.205-예제 5.8

컴파일러가 모르는 경우도 있지만, 그럴 때는 써주면 됨.

파라미터 중 일부 타입은 지정 가능.

 

람다를 바로 사용할 때) 람다 파라미터 이름을 디폴트 이름 it 으로 바꾸면 더 간단히. p.206-예제 5.9

tip! 람다가 중첩되는 경우에는 알아보기 힘드니까, 명시적으로 선언하는 게 더 낫다.

람다를 변수에 저장할 때) 파라미터의 타입 명시 > 파라미터의 타입을 추론할 문맥 존재 X

맨 마지막에 있는 식이 람다의 결과값이 된다.

 

4. 현재 영역에 있는 변수에 접근

Java & Kotlin

무명 내부 클래스& 람다: 메소드의 로컬 변수를 무명 내부 클래스에서 사용할 수 있다. p. 207 - 5.10

Java: final 변수만 = 값을 변경할 수 없다.

Kotlin: 모든 변수(val, var) = 값을 변경할 수 있다.(var) p. 208 - 5.11

 

람다가 포획한 변수

val, final 인 변수: 람다 코드를 변수 값과 함께 저장

var, final 이 아닌 변수: 변수를 특별한 Wrapper 로 감싸서 나중에 변경하거나, 읽을 수 있게 한 다음, 래퍼에 대한 람다 코드와 함께 저장

더보기

class Ref<T> (var value: T)

p.209 - 회색 박스

!!!!! 람다를 이벤트 핸들러/비동기 적으로 실행되는 코드로 활용하는 경우 !!!!! p. 210 - try Click...

함수 호출이 끝난 다음에 로컬 변수가 변경될 수도 있다. 

WHY ? 함수 안에 정의 된 로컬 변수의 생명주기는 함수가 반환되면 끝난다.

> (추측) 람다를 사용하는 함수가 사용이 끝나면, 메모리에서 사라지므로 !!

SOLUTION ? 클래스의 프로퍼티나 전역 프로퍼티 등의 위치로 빼서, 나중에 변수 변화를 살펴 볼 수 있게 한다.

> (추측) 생명주기를 클래스에 맞춘다!

 

5. 멤버 참조, ::

함수를 직접 넘기는 방법, 함수를 값으로

멤버 참조, member reference, ::

참조 대상이 함수인지 프로퍼티인지와는 관계없이, 뒤에 괄호를 넣으면 안된다.

에타 변환
함수 f <-> 람다 {x -> f(x)} 를 서로 바꿔쓴다.

 

최상위에 선언된 함수나 프로퍼티를 참조할 수 있다.(클래스 이름 생략) - p. 211

run(::salute) 

직접 위임 함수에대 대한 참조를 제공 - p. 212

생성자 참조: 클래스 생성 작업을 연기/저장 - ???? -p. 213

확장함수도 사용 가능

 

+ 바운드 멤버 참조 - p. 213 - 회색 박스

멤버 참조를 생성할 때, 클래스 인스턴스를 함께 생성 > 나중에 그 인스턴스에 대한 멤버를 호출 > 수신 대상 객체를 별도 지정 X

반응형