아이템 33. 생성자 대신 팩토리 함수를 사용하라
p. 213
예를 들어 코틀린 코루틴 라이브러리를 살펴보면, 거의 모든 코루틴 컨텍스트의 companion 객체가 컨텍스트를 구별할 목적으로 CoroutineContext.Key 인터페이스를 구현하고 있습니다.
코틀린 코루틴 라이브러리에서 코루틴 컨텍스트는 코루틴의 동작을 제어하는 데 중요한 역할을 합니다.
각 코루틴 컨텍스트 요소는 CoroutineContext
인터페이스를 구현하며, 각 요소는 키-값 쌍으로 저장됩니다.
여기서 CoroutineContext.Key
인터페이스가 중요한 역할을 합니다.
CoroutineContext와 Key
CoroutineContext
는 여러 컨텍스트 요소를 포함할 수 있는 인터페이스입니다.
각 요소는 키-값 쌍으로 저장되며, 이 키는 CoroutineContext.Key
를 통해 정의됩니다.
예를 들어, 디스패처나 이름 등의 컨텍스트 요소가 있을 수 있습니다.
Companion 객체와 Key 인터페이스 구현
코루틴 컨텍스트의 각 요소는 자신을 구별하기 위해 companion 객체로 CoroutineContext.Key 인터페이스를 구현합니다.
이를 통해 특정 타입의 컨텍스트 요소를 쉽게 식별하고 추출할 수 있습니다.
아이템 34. 기본 생성자에 이름 있는 옵션 아규먼트를 사용하라
p. 227
커링 (currying)
커링(currying)은 여러 개의 인자를 받는 함수가 실제로는 하나의 인자만을 받는 여러 함수의 연속으로 변환되는 기법입니다.
즉, 여러 인자를 받는 함수를 인자가 하나인 함수들의 체인으로 바꿀 수 있게 해주는 방법입니다.
커링을 통해 복잡한 함수 호출을 간단하게 만들고, 부분적으로 적용된 함수들을 생성할 수 있습니다.
커링의 개념
커링은 수학자 Haskell Curry의 이름을 따서 명명된 개념으로, 함수형 프로그래밍에서 자주 사용됩니다. 기본적인 커링의 개념은 다음과 같습니다
- 함수
f
가 두 개의 인자a
와b
를 받는다고 가정합니다. - 커링을 적용하면
f(a, b)
는(a) -> (b) -> f(a, b)
형태로 변환됩니다.
즉, 커링된 함수는 하나의 인자를 받고, 그 인자를 적용한 새로운 함수를 반환합니다.
커링 예제
// 두 개의 인자를 받는 함수
fun add(a: Int, b: Int): Int = a + b
// 커링을 적용한 함수
fun addCurried(a: Int): (Int) -> Int = { b -> a + b }
fun main() {
// 일반 함수 호출
val sum = add(3, 5)
println(sum) // Output: 8
// 커링된 함수 호출
val add3 = addCurried(3)
val result = add3(5)
println(result) // Output: 8
}
아이템 35. 복잡한 객체를 생성하기 위한 DSL을 정의하라
p. 232
리시버를 사용하는 함수 타입
개념
리시버를 사용하는 함수 타입은 특정 타입의 리시버 객체를 통해 호출될 수 있는 함수입니다.
즉, 함수가 리시버 객체의 멤버처럼 동작하게 됩니다.
이를 통해 더 자연스럽고 읽기 쉬운 DSL을 작성할 수 있습니다.
기본 문법
val lambda: String.() -> Unit = {
println(this)
}
예제 1
class HtmlTag(val name: String) {
private val children = mutableListOf<HtmlTag>()
fun html(init: HtmlTag.() -> Unit): HtmlTag {
val root = HtmlTag("html")
root.init()
return root
}
fun tag(name: String, init: HtmlTag.() -> Unit) {
val child = HtmlTag(name)
child.init()
children.add(child)
}
override fun toString(): String {
return "<$name>${children.joinToString("") { it.toString() }}</$name>"
}
}
fun main() {
val html = HtmlTag("html").html {
tag("body") {
tag("h1") {
tag("span") {}
}
tag("p") {}
}
}
println(html)
}
예제 2
class Person {
var name: String = ""
var age: Int = 0
}
fun person(init: Person.() -> Unit): Person {
val person = Person()
person.init()
return person
}
fun main() {
val person = person {
name = "John Doe"
age = 30
}
println("Name: ${person.name}, Age: ${person.age}")
}
p. 234
val myPlus: Int.(Int) -> Int = { this + it }
에서 this
와 it
이 가리키는 것은 무엇인가?
이 코드는 코틀린에서 리시버를 사용하는 함수 타입을 정의한 것입니다.
여기서 myPlus
는 Int
타입의 리시버를 가지며, 하나의 Int
인자를 받아 Int
를 반환하는 함수입니다.
이 함수 타입을 통해 Int
타입의 리시버 객체에서 직접 호출할 수 있는 확장 함수를 정의합니다.
리시버와 람다 파라미터의 의미
this
는 리시버 객체를 가리킵니다. 여기서는Int
타입의 리시버를 가리킵니다.it
는 람다 표현식의 암시적 파라미터 이름으로, 함수의 인자를 가리킵니다. 여기서는 함수에 전달된Int
타입의 값을 가리킵니다.
예제
fun main() {
val result = 5.myPlus(3)
println(result) // Output: 8
}
'독서' 카테고리의 다른 글
[이펙티브 코틀린] 7장. 비용 줄이기 (0) | 2024.08.04 |
---|---|
[이펙티브 코틀린] 6장. 클래스 설계 (0) | 2024.07.28 |
[이펙티브 코틀린] 4장. 추상화 설계 (5) | 2024.07.15 |
[이펙티브 코틀린] 3장. 재사용성 (0) | 2024.07.08 |
[이펙티브 코틀린] 2장. 가독성 (1) | 2024.06.30 |