람다란 무엇인가?
람다란 일종의 익명 함수이며, 로직이다. 함수에 로직을 넘기기 쉽게 하기 위해 활용
/* 람다가 없던 구형 자바
*/
button.setOnClickListener(new OnClickListener() {
@override
public void onClick(View view)
/* 클릭 시 수행할 동작 */
}
});
/* 자바의 람다식 */
button.setOnClickListener(/* (인자)-> 실행 문장 */)
Java
복사
•
setOnClickListener에 클릭 시 수행할 동작 하나를 전달하기 위해 특정 인터페이스를 구현하는 익명 클래스를 만들어야 하기 때문에 매우 복잡함
•
Java8 부터는 자바에서도 람다식을 도입함 → 코틀린은 해당 수식을 더 간결하게 해줌
코틀린의 람다식
button.setOnClickListener { /* 클릭 시 수행할 동작 */ }
Kotlin
복사
•
매우 간결해짐
•
함수 호출을 ()가 아닌 {}로 함
람다식의 장점
val persons = listOf(
Person("Captain", 44),
Person("Cyclops", 35),
Person("Deadpool", 31),
Person("Iceman", 54),
)
fun main() {
// 가장 나이가 많은 사람을 출력
println(persons.maxByOrNull { it.age })
}
== 결과 ==
Person(name=Iceman, age=54)
Kotlin
복사
•
매우 높은 간결성과 한눈에 보이는 가독성
람다식의 문법
fun sum(x: Int, y: Int): Int = x + y
Kotlin
복사
•
위의 함수를 람다식으로 변경하면
•
람다를 사용하는 방법
◦
함수에 파라미터 람다식 넘기기
◦
람다식을 변수에 저장하고 해당 식을 나중에 필요할 때 실행시키기
fun sum(x: Int, y: Int): Int = x + y
val sumLambda = { x: Int, y: Int -> x + y }
fun main() {
// 일반 함수 호출
println(sum(12, 34))
// 람다 변수 호출
println(sumLambda(12, 34))
// 람다 수식 바로 실행
println({ x: Int, y: Int -> x + y }(12, 34))
}
Kotlin
복사
람다 수식의 다양성, 간결화 수행
// 가장 정석적인 람다 호출, 람다 수식(중괄호)를 함수 호출에 인자로 넣어줌
println(persons.maxByOrNull({ person: Person -> person.age }))
// 가장 마지막 인자가 람다식이면 함수 호출 밖으로 뺄 수 있음
println(persons.maxByOrNull() { person: Person -> person.age })
// 람다만 있는 경우 빈괄호 삭제 가능
println(persons.maxByOrNull { person: Person -> person.age })
// 컴파일러가 타입 추론
println(persons.maxByOrNull { person -> person.age })
// 람다의 파라미터 이름을 기본 이름인 it으로 수정하면 가장 간결한 람다식이 완성
println(persons.maxByOrNull { it.age })
Kotlin
복사
•
단일 파라미터만 사용하는 람다식에서 주로 최종 단계 수준으로 활용
•
다중 파라미터이거나 컴파일러 타입 추론이 어려운 경우 그리고 깊이가 있는 수식인 경우엔 it이나 타입 추론을 사용하기 어려울 수 있음
멤버(함수) 참조
println(persons.maxByOrNull { person -> person.age })
// 이미 정의된 동일한 함수가 있으므로 해당 함수를 넘긴다
println(persons.maxByOrNull(Person::age))
Kotlin
복사
•
이중 콜론(::)을 활용해서 특정 클래스의 프로퍼티나 메서드를 참조할 수 있음
•
최상위 함수나 값의 경우 ::topLevelFunction 처럼 클래스명 지정 없이 바로 참조함
컬렉션 함수형 API
filter, map
•
가장 기초가 되는 함수
◦
filter : 데이터를 필터링
◦
map : 데이터를 다른 데이터로 변환(매핑)
•
다른 복잡한 함수들도 이것들을 활용하여 만들 수 있음
val persons = listOf(
Person("Captain", 44),
Person("Cyclops", 35),
Person("Deadpool", 31),
Person("Iceman", 54),
)
fun main() {
println(persons.filter { it.age > 36 })
println(persons.filter { it.age > 36 }
.map { "${it.name}'s age is ${it.age}" })
}
== 결과 ==
[Person(name=Captain, age=44), Person(name=Iceman, age=54)]
[Captain's age is 44, Iceman's age is 54]
Kotlin
복사
•
persons에서 나이가 36세 초과인 사람만 filtering
•
필터링 후 해당 결과를 String으로 maping(변환)
all, any, count, find
•
all : 모두 조건에 맞는가?
•
any : 하나라도 조건에 맞는가?
•
count : 조건에 맞는 원소의 개수를 구함
•
find : 조건에 맞는 원소를 하나만 주거나, 없으면 null을 응답
groupBy
•
특정 값으로 key를 만들고 해당 값에 해당하는 원소를 리스트로 갖는 map 객체를 만듦
val persons = listOf(
Person("Captain", 44),
Person("Cyclops", 35),
Person("Deadpool", 31),
Person("Iron Man", 31),
Person("Iceman", 54),
Person("Loki", 54),
Person("Hulk", 54),
)
fun main() {
println(persons.groupBy { it.age })
}
==== 결과 ====
{
44=[Person(name=Captain, age=44)],
35=[Person(name=Cyclops, age=35)],
31=[Person(name=Deadpool, age=31), Person(name=Iron Man, age=31)],
54=[Person(name=Iceman, age=54), Person(name=Loki, age=54), Person(name=Hulk, age=54)]
}
Kotlin
복사
flatMap, flatten
•
flatten : 중첩 컬렉션(List<List<T>>와 같은)를 단일 컬렉션(List<T>)로 만들어줌
•
flatMap
◦
map + flatten
◦
map 이후에 중첩 컬렉션이 발생되는 경우 flatMap을 활용하면 단일 컬렉션으로 결과를 바로 만들어줘서 편함
▪
col.map.flatten() → col.flatMap
◦
하지만 이런 용도로 실제 활용할 일은 잘 없고, 함수형 프로그래밍에서 매우 적극적으로 활용하게 됨
fun main() {
val strings = listOf("abc", "def")
println(strings.map { it.toList() })
println(strings.map { it.toList() }.flatten())
println(strings.flatMap { it.toList() })
}
==== 결과 ====
[[a, b, c], [d, e, f]]
[a, b, c, d, e, f]
[a, b, c, d, e, f]
Kotlin
복사
sequence
•
자바의 스트림과 같은 역할을 함
•
대량 컬렉션에 필터링이나 찾기 연산을 하는 경우 유용함
◦
sequence가 아닌 경우 무조건 모든 결과를 계산
◦
sequence는 조건에 맞는 경우, 결과가 필요한 경우에 한해서 계산을 진행
val persons = listOf(
Person("Captain", 44),
Person("Cyclops", 35),
Person("Deadpool", 31),
Person("Iceman", 54),
Person("Loki", 54),
Person("Hulk", 54),
)
fun main() {
println(
persons.map {
println(it)
it.name
}.find { it.startsWith("I") }
)
println(
persons.asSequence()
.map {
println(it)
it.name
}.find { it.startsWith("I")
)
}
==== 결과 ====
Person(name=Captain, age=44)
Person(name=Cyclops, age=35)
Person(name=Deadpool, age=31)
Person(name=Iceman, age=54)
Person(name=Loki, age=54)
Person(name=Hulk, age=54)
Iceman
Person(name=Captain, age=44)
Person(name=Cyclops, age=35)
Person(name=Deadpool, age=31)
Person(name=Iceman, age=54)
Iceman
Kotlin
복사
•
일반 컬렉션 : 모두 map을 진행한 후 find를 수행
•
sequence
◦
개별 원소마다 map과 find를 진행
◦
find에서 조건이 맞는 원소를 찾는 즉시 더 이상의 연산 수행하지 않음
•
대량의 원소에서 필터링이나 find를 수행할 때 활용하면 좋을 수 있음