이 포스팅은 Kotlin in Action, 드미트리 제메로프 & 스베트라나 이사코바, 에이콘출판사(2017)을 읽고 개인 학습용으로 정리한 글입니다.
val people = listOf(Person("Alice", 29), Person("Bob", 31))
//람다를 이용해 컬렉션 검색하기
println(people.maxBy{it.age})
//멤버 참조를 사용해 컬렉션 검색하기
println(people.maxBy(Person::age))
{x: Int, y:Int -> x+y}
코틀린 람다식은 항상 중괄호로 둘러싸여 있음
인자 목록 주변에 괄호 없음
화살표(->)가 인자 목록과 함수 본문 구분
run: 인자로 받은 람다를 실행해주는 라이브러리 함수
-> 코드의 일부분을 블록으로 둘러싸 실행할 필요가 있을 때 사용
실행 시점에 코틀린 람다 호출에 무가 비용X, 프로그램의 기본 구성 요소와 비슷한 성능
함수 호출 시 맨 뒤에 있는 인자가 람다 식 -> 그 람다를 괄호 밖으로 빼낼 수 O
람다가 어떤 함수의 유일한 인자이고 괄호 뒤에 람다 식 -> 빈 괄호 생략 가능
컴파일러는 람다 파라미터의 타입도 추론 가능
-> 처음에는 타입을 쓰지 않고 람다 작성 -> 컴파일러가 타입 추론 못하는 경우에만 타입 명시
람다릐 파라미터 디폴트 이름 it
-> 람다의 파라미터 하나 뿐이고 컴파일러가 그 타입을 추론할 수 있는 경우 사용 가능
람다를 함수 안에 정의하는 경우
-> 함수의 파라미터 접근 가능
-> 람다 정의의 앞에 선언된 로컨 변수 접근 가능
forEach: 컬렉션의 모든 원소에 대해 람다 호출
람다에서 람다 밖 함수에 있는 변수 변경 가능
-> 람다 안에서 사용하는 외부 변수: 람다가 포획한 변수
기본적으로 함수 안에 정의된 로컬 변수의 생명 주기: 함수가 반환되면 끝남
어떤 함수가 자신의 로컬 변수를 포획한 람다를 반환하거나 다른 변수에 저장하는 경우: 로컬 변수의 생명주기와 함수의 생명주기 달라질 수 O
-> ⭐포획한 변수가 있는 람다를 저장해서 함수가 끝난 뒤에 실행해도 람다의 본문 코드는 여전히 포획한 변수를 읽거나 쓸 수 O
ex. 람다를 이벤트 핸들러나 다른 비동기적으로 실행되는 코드로 활용하는 경우
-> 함수 호출이 끝난 다음에 로컬 변수 변경될 수 O
fun tryToCountButtonClicks(button: Button): Int{
var clicks = 0
button.onClick{ clicks++ }
return clicks
}
최상위에 선언된 함수/프로퍼티 참조 가능
-> ::멤버
생성자 참조: 클래스 생성 작업을 연기하거나 저장해둘 수 O
-> ::클래스 이름
확장 함수도 멤버 함수와 똑같은 방식으로 참조 가능
바운드 멤버 참조: 멤버 참조를 생성할 때 클래스 인스턴스를 함께 저장 -> 나중에 그 인스턴스에 대해 멤버 호출
filter 함수: 컬렉션을 이터레이션하면서 주어진 람다에 각 원소 넘겨 람다가 true를 반환하는 원소만 모음
(참/거짓을 반환하는 함수: predicate)
map 함수: 주어진 람다를 컬렉션의 각 원소에 적용한 결과를 모아서 새 컬렉션을 만든다
맵의 경우 키와 값을 처리하는 함수 따로 존재
-> filterKeys, mapKeys, filterValues, mapValues
find 함수: 조건을 만족하는 첫 번째 원소 반환, 만족하는 원소 전혀 없는 경우 null 반환
-> firstOrNull과 같음
count와 size: 원소의 개수만을 추적하는 경우 count가 훨씬 더 효율적
flatMap 함수:
-> 1. 인자로 주어진 람다를 컬렉션의 모든 객체에 적용(map)
-> 2. 람다를 적용한 결과 얻어지는 여러 리스트를 하나의 리스트로 모음(flatten)
flatten 함수: 단순히 리스트를 평평하게 펼침(= 여러 리스트를 하나의 리스트로 모음)
컬렉션 함수를 연쇄하면 매 단계마다 계산 중간 결과를 새로운 컬렉션에 임시로 담는다
시퀸스를 사용하면 중간 임시 컬렉션 사용 없이 컬렉션 연산 연쇄 가능
Sequence 안에는 iterator라는 단 하나의 메서드만 있다
시퀸스의 원소는 필요할 때 비로소 계산됨(=지연 계산)
-> 계산을 실행하게 만들려면 최종 시퀸스의 원소를 하나씩 이터레이션하거나 최종 시퀸스를 리스트로 변환
중간 연산: 다른 시퀸스 반환
-> 항상 지연 계산 됨
최종 연산: 결과 반환
-> 최종 연산을 호출하면 연기됐던 모든 계산 수행됨
⭐시퀀스의 경우 모든 연산은 각 원소에 대해 순차적으로 적용됨
컬렉션에 대해 asSequence() 호출
-> 모든 컬렉션에 대해 호출 가능
generateSequence 함수 사용
⚡시퀸스를 사용하는 일반적인 용례: 객체의 조상으로 이루어진 시퀸스
함수형 인터페이스/SAM 인터페이스
(SAM: 단일 추상 메서드)
코틀린은 함수형 인터페이스를 인자로 취하는 자바 메서드를 호출할 때 람다를 넘길 수 있다
함수의 인터페이스 구현으로 객체를 넘겨주는 경우
-> 메서드를 호출할 때마다 새로운 객체 생성됨
함수의 인터페이스 구현으로 람다를 넘겨주는 경우
-> 람다에 대응하는 무명 객체 메서드를 호출할 때마다 반복 사용
-> but 람다가 주변 영역의 변수를 포획하는 경우 반복 사용 X, 새로운 인스턴스 생성됨
코틀린 1.0에서 인라인(inline)되지 않은 모든 람다 식은 무명 클래스로 컴파일됨
-> 람다가 변수를 포획하면 무명 클래스 안에 포획한 변수를 저장하는 필드 생김
-> 매 호출마다 그 무명 클래스의 인스턴스 새로 만든다
SAM 생성자의 이름은 사용하려는 함수형 인터페이스의 이름과 같다
람다 안에서 this는 그 람다를 둘러싼 클래스의 인스턴스를 가리킨다
with 함수: 파라미터 2개
-> 첫번째 인자로 받은 객체를 두번째 인자로 받은 람다의 수신 객체로 만든다
-> 인자로 받은 람다 본문에서 this로 수신 객체 접근 가능
람다의 결과 대신 수신 객체가 필요한 경우 apply 함수 사용
apply 함수는 거의 with와 같지만, 항상 자신에게 전달된 객체를 반환
객체의 인스턴스를 만들면서 즉시 프로퍼티 중 일부를 초기화해야하는 경우 유용