5. 람다로 프로그래밍
#
5.1 람다 식과 멤버 참조#
5.1.1 람다 소개: 코드 블록을 함수 인자로 넘기기- 무명 내부 클래스를 코드를 함수에 넘기거나 변수에 저장할 수 있지만 상당히 번거로우나, 함수형 프로그래밍에서는 함수를 값처럼 다루면서 문제를 해결합니다.
- 람다를 메서드가 하나뿐인 무명 객체 대신 사용할 수 있습니다.
#
5.1.2 람다와 컬렉션- 코드에서 중복을 제거하는 것은 프로그래밍 스타일을 개선하는 중요한 방법 중 하나입니다.
- 때로는 람다를 멤버 참조로 대치가 가능합니다.
#
5.1.3 람다 식의 문법- 람다 식을 선언하는 문법은 다음과 같습니다.
- 코드의 일부분을 블록으로 둘러싸 실행할 필요가 있다면
run
을 사용합니다.
- 람다가 함수의 유일한 인자라면 괄호 없이 람다를 쓰는 것이 좋은 방법 중 하나입니다.
- 파라미터 중 일부의 타입은 지정하고 나머지 파라미터는 타입을 지정하지 않고 이름만 남겨둬도 됩니다.
- 람다의 파라미터 이름을 디폴트 이름인
it
으로 바꾸면 람다 식을 더 간단하게 만들 수 있습니다.
#
5.1.4 현재 영역에 있는 변수에 접근- 자바 메서드 안에서 무명 내부 클래스를 정의할 때 메서드의 로컬 변수를 무명 내부 클래스에서 사용할 수 있습니다.
- 자바와 다른 점 중 중요한 한 가지는 코틀린 람다 안에서 파이널 변수가 아닌 변수에 접근할 수 없습니다.
- 코틀린에서는 자바와 달리 람다에서 람다 밖 함수에 있는 파이널이 아닌 변수에 접근할 수 있고, 그 변수를 변경할 수 있습니다.
#
5.1.5 멤버 참조- 코틀린에서는 자바 8과 마찬가지로 함수를 값으로 바꿀 수 있습니다.
::
를 사용하는 식을 멤버 참조(member references)라고 부릅니다.
- 생성자 참조(constructor reference)를 사용하면 클래스 생성 작업을 연기하거나 저장해둘 수 있습니다.
바운드 멤버 참조
- 바운드 멤버 참조를 사용하면 멤버 참조를 생성할 때 클래스 인스턴스를 함께 저장한 다음 나중에 인스턴스에 대해 멤버를 호출해줍니다.
#
5.2 컬렉션 함수형 API#
5.2.1 필수적인 함수: filter와 map#
5.2.2 all, any, count, find,: 컬렉션에 술어 적용#
5.2.3 groupBy: 리스트를 여러 그룹으로 이뤄진 맵으로 변경#
5.2.4 flatMap과 flatten: 중첩된 컬렉션 안의 원소처리-flatMap
함수는 인자로 주어진 람다를 컬렉션의 모든 객체에 적용하고 람다를 적용한 결과 얻어지는 여러 리스트를 한 리스트로 모읍니다.
- 특별히 변환해야 할 내용이 없는 경우, 리스트를 평평하게 펼치면 되는
flatten
함수를 사용할 수 있습니다.
#
5.3 지연 계산(lazy) 컬렉선 연산map
이나filter
같은 몇 가지 컬렉션 함수는 결과 컬렉션을 즉시(eagerly) 생성합니다.- 효율적으로 사용하기 위해서는 시퀀스를 사용하는 것이 중요합니다.
큰 컬렉션에 대해서 연산을 연쇄시킬 때는 시퀀스를 사용하는 것을 규칙으로 삼습니다.
#
5.3.1 시퀀스 연산 실행: 중간연산과 최종 연산- 시퀀스에 대한 연산은 중간(intermediate) 연산과 최종(terminal) 연산으로 나뉩니다.
- 시퀀스를 사용하면 지연 계산으로 인해 원소 중 일부의 계산이 이뤄지지는 않습니다.
- 컬렉션에 대해 수행하는 연산의 순서도 성능에 영향을 끼칩니다.
자바 스트림과 코틀린 시퀀스의 개념이 비슷합니다.
#
5.3.2 시퀀스 만들기- 시퀀스를 만드는 예제로
asSequence()
를 호출할 수도 잇고,generateSequence
함수를 사용할 수도 있습니다.
#
5.4 자바 함수형 인터페이스 활용- 코틀린에서는 무명 클래스 인스턴스 대신 람다를 넘길 수 있습니다.
- 위의 코드가작동하는 이유는 추상 메서드가 단 하나만 있기 때문이며, 이러한 인터페이스를 함수형 인터페이스(functional interface) 혹은 SAM 인터페이스(single abstract method interface)라고 합니다.
- 자바와 달리 코틀린에는 함수 타입이 존재합니다.
#
5.4.1 자바 메서드에 람다를 인자로 전달- 함수형 인터페이스를 인자로 원하는 자바 메서드에 코틀린 람다를 전달할 수 있습니다.
- 람다와 무명 객체 사이에는 차이가 있습니다. 객체를 명시적으로 선언하는 경우 메서드를 호출할 때마다 새로운 객체가 생성됩니다. 람다는 반복 사용합니다.
코틀린 1.0에서 인라인(inline) 되지 않은 모든 람다 식은 무명 클래스로 컴파일됩니다.
- 대부분의 경우, 람다와 자바 함수형 인터페이스 사이의 변환은 자동으로 이뤄집니다.
#
5.4.2 SAM 생성자: 람다를 함수형 인터페이스로 명시적으로 변경- SAM 생성자는 람다를 함수형 인터페이스의 인스턴스로 변환할 수 있게 컴파일러가 자동으로 생성한 함수입니다.
- 람다로 생성한 함수형 인터페이스 인스턴스를 변수에 저장해야 하는 경우에도 SAM 생성자를 사용할 수 있습니다.
- 람다에는 무명 객체와 달리 인스턴스 자신을 가리키는
this
가 없습니다. - 람다안에서
this
는 그 람다를 둘러싼 클래스의 인스턴스를 가리킵니다. - 이벤트 리스너가 이벤트를 처리하다가 자기 자신의 리스너 등록을 해제해야 한다면 람다를 사용할 수 없으므로, 무명 객체를 통해 리스너를 구현해야 합니다.
#
5.5 수신 객체 지정 람다: with와 apply- 수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메서드를 호출할 수 있게 하는 것입니다. 이러한 람다를
수신 객체 지정 람다(lambda with receiver)
라고 부릅니다.
#
5.5.1 with 함수- with 함수는 첫번째 인자로 받은 객체를 두 번째 인자로 받은 람다의 수신 객체로 만듭니다.
수신 객체 지정 람다와 확장 함수 비교
일반 함수 | 일반 람다 |
---|---|
확장함수 | 수신 객체 지정 람다 |
- 람다는 일반 함수와 비슷한 동작을 정의하는 한 방법입니다.
- 불필요한 stringBuilder 변수를 없애면 alphabet 함수가 식의 결과를 바로 반환하게 됩니다.
메서드 이름 충돌
- 바깥쪽 클래스에 정의된 함수를 사용하고 싶으면,
this@OuterClass.method()
와 같이 사용해야합니다.
#
5.5.2 apply 함수- apply 함수는 거의 with과 같으나, 항상 자신에게 전달된 객체를 반환한다는 점입니다.
- with과 apply는 수신 객체 지정 람다를 사용하는 일반적인 예제 중 하나입니다.
#
5.6 요약- 람다를 사용하면 코드 조각을 다른 함수에게 인자로 넘길 수 있습니다.
- 코틀린에서는 람다가 함수 인자인 경우 괄호 밖으로 람다를 빼낼 수 있고, 람다의 인자가 단 하나뿐인 경우 인자 이름을 지정하지 않고
it
이라는 디폴트 이름으로 부를 수 있습니다. - 람다 안에 있는 코드는 그 람다가 들어있는 바깥 함수의 변수를 읽거나 쓸 수 있습니다.
- 메서드, 생성자, 프로퍼티의 이름 앞에 ::을 붙이면 각각에 대한 참조를 맏르 수 있습니다. 이러한 참조를 람다 대신 다른 함수에게 넘길 수 있습니다.
filter, map, all, any
등의 함수를 활용하면 컬렉션에 대한 대부분의 연산을 직접 원소를 이터레이션하지 않고 수행할 수 있습니다.- 시퀀스를 사용하면 중간 결과를 담는 컬렉션을 생성하지 않고도 컬렉션에 대한 여러 연산을 조합할 수 있습니다.
- 함수형 인터페이스를 인자로 받는 자바 함수를 호출할 경우 람다를 함수형 인터페이스 인자 대신 넘길 수 있습니다.
- 수신 객체 지정 람다를 사용하면 람다 안에서 미리 정해둔 수신 객체의 메서드를 직접 호출할 수 있습니다.
- 표준 라이브러리의
with
함수를 사용하면 어떤 객체에 대한 참조를 반복해서 언급하지 않으면서 그 객체의 메서드를 호출할 수 있습니다.apply
를 사용하면 어떤 객체라도 빌더 스타일의 API를 사용해 생성하고 초기화할 수 있습니다.