728x90
반응형
✅ 자바 함수형 인터페이스 활용
- 코틀린 라이브러리와 람다를 활용하는 것도 좋지만 우리가 다뤄야 할 API 중 상당수는 코틀린이 아닌 자바로 작성되었을 가능성이 높다.
- 다행인 점은 코틀린 람다를 자바 API에 사용해도 아무 문제가 없다.
- 그렇다면 어떻게 코틀린 람다를 자바 API에 활용할 수 있을까?
📌 자바 메소드에 람다를 인자로 전달
- 함수형 인터페이스를 인자로 원하는 자바 메서드에 코틀린 람다를 전달할 수 있다.
- 예를 들면 다음 메서드는 Runnable 타입의 파라미터를 받는다.
/* 자바 */
void postponeComputation(int delay, Runnable computation);
/* 코틀린에서 람다를 이 함수에 넘길 수 있다. */
postponeComputation(1000) { println(42) }
- 여기서 'Runnable 인스턴스'라는 말은 실제로는 'Runnable을 구현한 무명 클래스의 인스턴스'라는 뜻이다.
- 컴파일러는 자동으로 무명 클래스와 인스턴스를 만들어준다.
- 이때, 그 무명 클래스에 있는 유일한 추상 메서드를 구현할 때 람다 본문을 메서드 본문으로 사용한다.
- 이 예제에서는 Runnable의 run
- 이때, 그 무명 클래스에 있는 유일한 추상 메서드를 구현할 때 람다 본문을 메서드 본문으로 사용한다.
- Runnable을 구현하는 무명 객체를 명시적으로 만들어 사용할 수도 있다.
postponeComputation(1000, object : Runnable { // 객체 식을 함수형 인터페이스 구현으로 넘긴다.
override fun run() {
println(42)
}
})
postponeComputation(1000) {println(42)} // 프로그램 전체에서 Runnable의 인스턴스는 단 하나만 만들어진다.
- 하지만 람다와 무명 객체 사이에는 차이가 있다.
- 객체를 명시적으로 선언하는 경우 메서드를 호출할 때마다 새로운 객체가 생성되지만 람다는 다르다.
- 정의가 들어있는 함수의 변수에 접근하지 않는 람다에 대응하는 무명 객체를 메소드를 호출할 때마다 반복 사용한다.
- 즉, 객체를 한 번만 생성하여 이를 활용하기 때문에 효율적이다.
- 따라서 명시적인 object 선언을 사용하면서 람다와 동일한 식은 다음과 같다.
- 이 경우 Runnable 인스턴스를 변수에 저장하고 메서드를 호출할 때마다 그 인스턴스를 사용한다.
val runnable = Runnable { println(42) } // Runnable은 SAM 생성자 -> 전역 변수로 컴파일되므로
fun handleComputation() { // 프로그램 안에 단 하나의 인스턴스만 존재한다.
postponeComputation(1000, runnable) // 모든 handleComputation 호출에 같은 객체를 사용
- 람다가 주변 영역의 변수를 포획할 경우 매 호출마다 같은 인스턴스를 사용할 수 없다.
- 그런 경우 컴파일러는 매번 주변 영역의 변수를 포획한 새로운 인스턴스를 생성해준다.
- 아래 예시와 같이 id를 필드로 저장하는 새로운 Runnable 인스턴스를 매번 새로 만들어 사용한다.
fun handleComputation(id: String) { // 람다 안에서 "id" 변수를 포획한다.
postponeComputation(1000) { println(id) } // handlecomputation을 호출할 때마다 새로 Runnable 인스턴스를 만든다.
}
📌 SAM 생성자 : 람다를 함수형 인터페이스로 명시적으로 변경
- SAM 생성자는 람다를 함수형 인터페이스의 인스턴스로 변환할 수 있게 컴파일러가 자동으로 생선한 함수이다.
- 컴파일러가 자동으로 람다를 함수형 인터페이스 무명 클래스로 바꾸지 못하는 경우 SAM 생성자를 사용할 수 있다.
- 예를 들면, 함수형 인터페이스의 인스턴스를 반환하는 메서드가 있고 람다를 직접 반환할 수 없다면 반환하고픈 람다를 SAM 생성자로 감싸야 한다.
/* SAM 생성자를 사용해 값 반환하기 */
fun createAllDoneRunnable() : Runnable { // createAllDoneRunnable() : Runnable 객체 반환하는 메소드
return Runnable { println("All done!") }
}
createAllDoneRunnable().run()
// All done!
- SAM 생성자의 이름은 사용하려는 함수형 인터페이스의 이름과 같다.
- SAM 생성자는 그 함수형 인터페이스의 유일한 추상 메서드의 본문에 사용할 람다만을 인자로 받아 함수형 인터페이스를 구현하는 클래스의 인스턴스를 반환한다.
- 또한, 람다로 생성한 함수형 인터페이스 인스턴스를 변수에 저장해야 하는 경우에도 SAM 생성자를 사용한다.
- 여러 버튼에 같은 리스너를 적용하고 싶다면 아래와 같이 SAM 생성자를 통해 람다를 함수형 인터페이스 인스턴스로 만들어 변수에 저장해 활용할 수 있다.
/* SAM 생성자를 사용해 listener 인스턴스 재사용하기 */
val listener = OnClickListener { view ->
val text = when (view.id) { // view.id를 사용해 어떤 버튼이 클릭됐는지 판단
R.id.button1 -> "First Button"
R.id.button2 -> "Second Button"
else -> "Unknown Button"
}
toast(text) // "text"의 값을 사용자에게 보여준다.
}
button1.setOnClickListener(listener)
button2.setOnClickListener(listener)
728x90
반응형
'Studying > Kotlin' 카테고리의 다른 글
[Kotlin In Aciton] 6장. 코틀린 타입 시스템(1) - 널 가능성[1] (0) | 2023.05.25 |
---|---|
[Kotlin In Action] 5장. 람다로 프로그래밍(5) - 수신 객체 지정 람다 : with와 apply (0) | 2023.05.23 |
[Kotlin In Action] 5장. 람다로 프로그래밍(3) - 지연 계산(lazy) 컬렉션 연산 (0) | 2023.05.22 |
[Kotlin In Action] 5장. 람다로 프로그래밍(2) - 컬렉션 함수형 API (0) | 2023.05.18 |
[Kotlin In Action] 5장. 람다로 프로그래밍(1) - 람다 식과 멤버 참조 (0) | 2023.05.17 |