본문 바로가기
Kotlin

Kotlin in action 5장 람다로 프로그래밍

by 박새영 2020. 4. 9.

람다? 람다 식(lambda expression)?

 

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

코드 블록을 함수 인자로 넘기기

 

값처럼 여기저기 전달할 수 있는 동작의 모음

함수형 언어에서 함수를 직접 다른 함수에 전달하기.

 

+추가

익명클래스로 선언된 구현부를 변수처럼 쓸수 있게 해준다.

(변수처럼? 여기저기 넣을수있음)

 

람다 식을 사용하면 코드가 더 간결해짐.

→함수를 선언할 필요가 없고 코드 블록을 직접 함수의 인자로 전달할 수 있다.

 

 

 

 

 

예제

-버튼 클릭에 따른 동작 정의하는 경우 클릭 이벤트를 처리하는 리스너를 추가한다.

button.setOnClickListener(new OnClickListener() {
	@Override public void onClick(View view) { 
  		/* 클릭 시 수행할 동작 */
    }	 
}//무명 내부 클래스 선언하느라 번잡한 코드


button.setOnClickListener {
	/* 클릭 시 수행할 동작 */
}// 람다로 리스너 구현

→람다를 메소드가 하나뿐인 무명 객체 대신 사용할 수 있다.

 

 

 

 

 

멤버 참조를 인자로 취하는 것

 

멤버 참조?

::를 사용하는 식을 멤버 참조라고 한다.

클래스::멤버

(메소드, 생성자, 프로퍼티의 이름 앞에 ::을 붙이면 각각에 대한 참조를 만들 수 있다.)

people.maxBy(Person::age)

 

: 프로퍼티나 메소드를 단 하나만 호출하는 함수 값을 만들어준다.

→함수를 직접 넘겨 주기

 

그런 참조를 람다 대신 다른 함수에게 넘길 수 있다.

 

 

함수의 인자를 람다나 멤버 참조로 받게 만든 코드는 더 짧고 더 이해하기 쉽다

 

 

 

 

 

람다 식 문법

{ x: Int, y: Int → x+y }

 

코틀린 람다 식은 항상 중괄호로 둘러싸여 있다

→ 화살표가 인자 목록과 람다 본문을 구분해준다.

 

 

 

 

 

람다 식을 변수에 저장하기

val sum = { x: Int, y: Int → x+y }
println( sum(1, 2) ) // 변수에 저장된 람다를 호출한다.

 

 

 

 

 

코틀린에는 함수 호출 시 맨 뒤에 있는 인자가 람다 식이라면

 

그 람다를 괄호 밖으로 빼낼 수 있다는 문법 관습이 있다.

 

people.maxBy() { p: Person → p.age }

 

 

 

 

 

 

람다가 어떤 함수의 유일한 인자이고,

 

괄호 뒤에 람다를 썼다면 호출 시 빈 괄호를 없애도 된다.

 

people.macBy {p: Person → p.age }

 

 

 

 

 

 

람다가 함수의 유일한 인자라면 괄호 없이 람다를 바로 쓰기를 원하게 된다.

 

컴파일러는 람다 파라미터의 타입도 추론할 수 있다.

 

people.maxBy { p: Person → p.age }

people.maxBy { p → p.age } // 타입 추론이 가능해서 : Person 생략!

 

 

 

 

 

 

 

디폴트 파라미터 이름 it 사용하기

people.maxBy { it.age }

 

 

 

람다에서 변수 포획하기.

 

변수를 포획한다? 포획한 변수?

→람다 안에서 사용하는 외부 변수를 람다가 포획한 변수라고 부른다.

 

코틀린에서는 람다에서 람다 밖 함수에 있는 변수에 접근할 수 있고, 그 변수를 변경할 수도 있다.

 

 

자바에서는 파이널 변수만 포획할 수 있다.

 

자바에서 변경 가능한 변수를 포획하는 방법(속임수)은

 

변경 가능한 변수를 저장하는 원소가 단 하나뿐인 배열을 선언하거나,

 

변경 가능한 변수를 필드로 하는 클래스를 선언.

→"변경 가능한 변수를 필드로 하는 클래스를 선언" → 이렇게 코틀린이 컴파일 된다.

 

var counter = 0 val inc = { counter++ } // 코틀린으로 이렇게 작성하면?



class Ref<T>(var value: T) // 이렇게 컴파일 된다.

val counter = Ref(0)
val inc = { counter.value++ }

 

 

 

시퀀스 인터페이스

 

 

(컬렉션 연산을 연쇄 실행하는)연쇄 호출에서 리스트를 2개 만든다면 시퀀스를 사용하면 효율적이다.

 

시퀀스 안에는 iterator 라는 단 하나의 메소드가 있다.

 

그 메소드를 통해 시퀀스로부터 원소 값을 얻을 수 있다.

 

시퀀스의 원소는 필요할 때 계산된다.

→시퀀스에 대한 연산을 지연 계산하기 때문에 정말 계산을 실행하게 하려면 최종 시퀀스의 원소를

하나씩 이터레이션하거나 최종 시퀀스를 리스트로 변환해야한다.

 

OnClickListener에 추상 메소드가 단 하나만 있기 때문이다 (onClick)

 

그런 인터페이스를 함수형 인터페이스 또는 SAM 인터페이스라고 한다.

 

SAM은 단일 추상 메소드 Single Abstract Method 라는 뜻이다.

 

코틀린은 함수형 인터페이스를 인자로 취하는 자바 메소드를 호출할 때 람다를 넘길 수 있게 해준다.

 

수신 객체 지정 람다

 

자신에게 전달된 객체 →수신 객체

 

with(수신객체) {람다 식}

: 인자를 두개 받는데

첫번째 파라미터 → 수신객체

두번째 파라미터 → 람다 식으로 받는것임.

 

with() 가 반환하는 값

→수신객체를 인자로 받아서 람다 코드를 실행한 결과. 마지막 메소드를 결과 값으로 반환.

→it 주의

 

수신객체.apply() {람다 식}

apply() 가 반환하는 값

→수신객체를 반환함. 확장함수로 정의되어 있다.

→ 프로퍼티 초기화할 때 많이 사용하는 함수.

 

 

 

 

 

fun tryToCountButtonClicks (button: Button) : Int {
	var clicks = 0
    
    button.onClick { clicks++ }
    // button.setOnClickListener(new onClick() { clicks++ } );
    
    return clicks
}

비동기

 

→ 이 함수는 항상 0을 반환한다.

onClick 핸들러는 호출될 때마다 clicks의 값을 증가시키지만 그 값의 변경을 관찰할 수는 없다.

관찰?

onClick은 tryToCountButtonClicks가 clicks를 반환한 다음에 호출되기 때문이다.

tryToCountButtonClicks는 호출하면 바로 clicks를 return하고

onClick은 click을 기다렸다가 클릭 되면 그때서야 나~중에 clicks++ 되기 때문이다.