독서

[코틀린 인 액션] 5장. 람다로 프로그래밍

오렌지색 귤 2024. 4. 14. 14:41
반응형

찾아본 내용

 

1. 주요 용어

 

  1. 무명 클래스 (Anonymous Class)

 

무명 클래스는 이름이 없는 클래스로, 주로 일회성으로 사용되는 객체를 생성할 때 사용됩니다. 이 클래스는 주로 이벤트 리스너나 작은 인터페이스 구현에 사용되며, Java와 유사한 방식으로 코틀린에서도 사용됩니다.

 

val thread = Thread(object : Runnable {
    override fun run() {
        println("Thread running")
    }
})
thread.start()

 

이 예에서 Runnable 인터페이스를 구현하는 무명 클래스를 만들어 Thread 생성자에 전달했습니다.

 

  1. 람다 (Lambda)

람다는 간결한 방식으로 함수를 표현하는 방법입니다. 람다 표현식을 사용하면 별도의 클래스를 정의하지 않고도 기능을 인라인으로 전달할 수 있습니다.

 

val list = listOf(1, 2, 3)
list.forEach { item -> println(item) }

 

이 코드는 리스트의 각 항목을 출력하는 람다 표현식을 forEach 함수에 전달합니다. 람다 내부에서는 각 항목을 item이라는 파라미터로 접근합니다.

 

  1. 포획 (Capturing)

 

람다 또는 무명 클래스가 외부의 변수를 사용하는 경우, 그 변수를 "포획(capture)"한다고 합니다. 포획은 람다나 무명 클래스가 외부 스코프의 변수를 참조할 수 있도록 해주며, 이러한 행위는 클로저를 생성합니다.

 

var count = 0
val increment = { count += 1 }
increment()
println(count) // 출력: 1

 

  1. 클로저 (Closure)

클로저는 함수와 그 함수가 포획한 외부 변수의 조합입니다. 즉, 클로저는 자신이 생성될 때의 환경을 "기억"하는 함수입니다.

 

예제


위의 포획 예제에서 increment 람다는 count 변수를 포획하여 클로저를 형성합니다. 이 클로저는 count의 상태를 유지하며, increment가 호출될 때마다 count를 변경할 수 있습니다.

 

 

 

2. Null 안전 연산자

  1. 안전 호출 연산자 ?.: 객체가 널이 아닐 경우에만 메서드나 속성에 접근하고, 객체가 널일 경우에는 널을 반환합니다.
val age = person?.age // person이 null이면 age는 null이 됨

 

  1. 엘비스 연산자 ?:: 좌측 표현식의 결과가 널이 아니면 그 결과를 사용하고, 널이면 우측의 값을 반환합니다.
val age = person?.age ?: 0 // person이 null이거나 person.age가 null이면 0을 반환

 

  1. 세이프 캐스트 연산자 (as?): 타입 캐스팅을 시도하고, 캐스팅이 실패하면 널을 반환합니다.
val person = obj as? Person // obj가 Person 타입이 아니면 null 반환

 

!! 연산자

 

!! 연산자는 강제 널 해제(Non-null assertion) 연산자로, 표현식의 결과가 널이 아님을 강제로 선언합니다. 이 연산자는 해당 변수 또는 표현식이 절대 널이 아닐 것이라는 것을 개발자가 보장할 때 사용합니다. 만약 해당 변수나 표현식의 결과가 널이면, 코틀린은 NullPointerException을 발생시킵니다.

 

val maxAge = people.maxBy(Person::age)!!.age

 

 

 

maxBy 결과가 null일 수 있기 때문에 반환 타입이 널 가능성을 가진 Person? 타입입니다. 여기서 !! 연산자를 사용하여 maxBy의 결과가 널이 아니라고 강제로 선언하고 있습니다. 이는 만약 people이 비어 있을 경우 maxBynull을 반환하게 되어 NullPointerException이 발생할 수 있음을 의미합니다.


!! 연산자는 신중하게 사용해야 하며, 가능하면 널 안전 연산자를 사용하는 것이 좋습니다.

 

 

3. 코틀린의 asSequence()와 자바에서의 stream() (p.223)

 

자바의 Stream

  • 자바의 Stream API는 자바 8부터 도입되었으며, 컬렉션의 데이터를 함수형 스타일로 처리할 수 있도록 설계되었습니다.
  • Stream은 데이터 소스에 대해 파이프라인을 구성할 수 있으며, filter, map, sorted 등의 중간 연산과 collect, forEach, reduce 등의 종단 연산을 제공합니다.
  • 중간 연산은 지연 평가되며, 종단 연산이 호출될 때 실제 계산이 수행됩니다.
  • 스트림은 한 번 사용하고 나면 재사용할 수 없으며, 데이터를 병렬로 처리하는 parallelStream()도 지원합니다.

 

코틀린의 Sequence

  • 코틀린의 SequenceasSequence()를 통해 생성되며, 이 또한 지연 평가를 사용하여 연산을 수행합니다.
  • Sequence는 자바의 Stream과 비슷하게 map, filter, sorted 등의 연산을 지원하고, 이러한 연산들은 중간 연산으로, 실제로 값이 필요할 때까지 계산을 지연합니다.
  • 코틀린의 Sequence는 특히 코틀린이 지원하는 다양한 플랫폼(자바, 자바스크립트, 네이티브)에서 사용할 수 있도록 설계되었습니다.
  • Sequence는 또한 한 번 생성하면 여러 번 사용할 수 있습니다. 이는 자바의 스트림과 다른 점 중 하나입니다.
  • 코틀린의 Sequence는 병렬 처리를 직접적으로 지원하지 않지만, 코틀린의 멀티플랫폼 특성과 호환성을 위해 설계되었습니다.

 

주요 차이점

  • 재사용성: 코틀린의 Sequence는 재사용이 가능하며, 자바의 Stream은 한 번 사용 후 재사용할 수 없습니다.
  • 병렬 처리: 자바의 StreamparallelStream()을 통해 병렬 처리를 명시적으로 지원합니다. 코틀린의 Sequence는 이러한 병렬 처리 기능이 내장되어 있지 않습니다.
  • 플랫폼 호환성: 코틀린의 Sequence는 코틀린이 지원하는 모든 플랫폼에서 일관된 API를 제공합니다.

 

 

 

4. SAM 생성자와 코틀린 컴파일러가 람다를 무명클래스로 변환하는 기준 (p. 233)

 

코틀린에서 SAM(Single Abstract Method) 생성자와 람다 표현식이 무명 클래스(Anonymous Class)로 변환되는 과정은 코틀린의 Java 상호 운용성 기능 중 중요한 부분입니다. 이는 코틀린이 자바의 함수형 인터페이스와의 통합을 원활하게 하기 위해 설계된 메커니즘입니다.

 

SAM 생성자

 

SAM 생성자는 코틀린에서 자바의 SAM 인터페이스를 구현할 때 사용되는 개념입니다. SAM 인터페이스란, 단 하나의 추상 메소드만을 가진 인터페이스를 의미합니다. 자바에서는 람다 표현식을 통해 이러한 인터페이스를 간단하게 구현할 수 있으며, 코틀린도 자바와의 호환을 위해 비슷한 기능을 제공합니다.

 

코틀린에서의 SAM 변환


코틀린에서는 자바의 SAM 인터페이스를 람다로 표현할 때 자동으로 SAM 변환을 수행합니다. 이는 람다 표현식이 자바의 SAM 인터페이스를 구현한 무명 클래스로 변환되는 과정을 말합니다. 하지만 이는 자바 인터페이스에만 적용되며, 코틀린에서 정의된 인터페이스는 이런 방식으로 변환되지 않습니다.

 

// Java
public interface Runnable {
    void run();
}

// Kotlin
val runnable = Runnable { println("Running") }

 

위의 예제에서 코틀린 람다 { println("Running") }는 Runnable 인터페이스를 구현하는 무명 클래스로 변환됩니다.

 

람다를 무명 클래스로의 변환

 

코틀린 컴파일러는 람다 표현식을 자바의 바이트코드로 컴파일할 때 일반적으로 무명 클래스로 변환합니다. 이 과정은 다음과 같은 기준에 따라 이루어집니다:

 

  1. 포획 없는 람다: 포획(capture)이 없는 람다, 즉 외부 변수를 사용하지 않는 람다는 모든 인스턴스에서 재사용 가능한 단일 객체로 컴파일됩니다.
  2. 포획 있는 람다: 외부의 변수를 포획하는 람다는 각 인스턴스 생성 시마다 새로운 무명 클래스 인스턴스를 생성합니다. 포획하는 변수에 따라 무명 클래스 내에서 해당 변수를 저장하는 필드가 추가됩니다.

 

var counter = 0
val increment = { counter += 1 }

 

이 경우, increment 람다는 counter 변수를 포획하기 때문에, 컴파일러는 이 람다를 counter 필드를 가진 무명 클래스로 변환합니다.

 

 

결론

 

코틀린의 SAM 생성자와 람다 표현식의 무명 클래스 변환은 코틀린의 자바 상호 운용성의 핵심 요소입니다. SAM 변환은 자바의 함수형 인터페이스와 쉽게 통합되도록 도와주며, 람다 표현식의 무명 클래스 변환은 코틀린 코드가 자바 바이트코드로 효율적으로 실행될 수 있도록 합니다. 이러한 변환은 코틀린이 자바 생태계 내에서 더욱 강력하고 유연하게 활용될 수 있도록 해줍니다.

반응형