codepro 때문에 포스팅을 나눠서 계속 짤라먹어야 하는게 짜증나네.
delegation :
https://kotlinlang.org/docs/delegation.html
https://kotlinlang.org/docs/delegated-properties.html
위임(delegation) 디자인 패턴( https://en.wikipedia.org/wiki/Delegation_pattern )은 한 클래스의 멤버 구현을 직접 하지 않고, 다른 클래스의 멤버구현을 이용하는 것이다. 다시말하면, 다른 클래스에게 위임하는 것이다. 코틀린은 이를 언어 수준에서 직접적으로 지원한다. 바로 ‘by’키워드가 그것이다.
interface ClosedShape {
fun area(): Int
}
class Rectangle(val width: Int, val height: Int) : ClosedShape {
override fun area() = width * height
}
// The ClosedShape implementation of Window delegates to that of the Rectangle that is bounds
class Window(private val bounds: Rectangle) : ClosedShape by bounds
위 코드에서 Window 클래스는 ClosedShape 인터페이스의 구현을 bounds에게 위임하고 있다.
클래스 단위가 아니라 property에서도 가능한데, 매우 흔히 볼 수 있다. 이 때, get()/set() 이 위임되는 대상의 getValue(), setValue()를 대신 부르게 되므로 이들이 구현되어 있어야 한다.
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
class Example {
var p: String by Delegate() // val/var <property name>: <Type> by <expression>
}

Lazy initialization
앞에서 delegated property 를다뤘는데, 코틀린 표준 라이브러리에서 지원해주는 것들이 있다. 그 중 하나가 Lazy이다.
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
이는 지연된 초기화에 사용되는데, lambda 함수를 인자로 받기 때문에, 여기서 초기화 코드를 전달해주면 된다. 처음 property가 불리면 get()이 실행되어 넘겨준 람다 함수가 실행되고 그 결과를 기억해둔다. 그 다음부터는 기억된 결과값만 돌려주게 된다.

scope functions
스코프 함수들은 let, run, with, apply, also 의 다섯가지가 있다. 사용에 혼동이 올 수 있는데, 정리내용이 좀 많으므로, 기존 포스팅으로 대체한다.
http://batmask.dothome.co.kr/index.php/2021/12/10/286/

coroutine : https://kotlinlang.org/docs/coroutines-overview.html
코틀린의 핵심 내용중 하나이지만, 이것도 내용이 많다. 기존에 작성했던 별도 포스팅 참고.

Flow : https://kotlinlang.org/docs/flow.html
코루틴과 함께 봐야할 역시 핵심내용. 이것도 내용이 많으니 기존 작성했던 포스팅 참고.
Kotlin : Flow Part.2 (Shared Flow, State Flow)

annotation : https://kotlinlang.org/docs/annotations.html
annotation은 코드에 메타 데이터를 붙이는걸 의미한다. 처음엔 간단한 에러나 경고표시 안하게 만드는 정도의 수준으로 알았었는데, 어느순간 특히 코틀린에서는 compose를 쓰려면 꼭 필요하듯이 코딩의 한 부분이 되버렸다. 예전 언어들의 프리프로세서 작업들이 문제가 많아 사라졌지만, 결국 이것들이 annotation으로 재탄생한 느낌도 든다. 실제로 annotation은 메인 코드에 영향을 미치지 않게 컴파일러에 의해 전처리 된다.
annotation을 만들려면 클래스 앞에 ‘annotation’을 붙여주면 된다.
annotation class Special
// usage
@Special class MyClass { }
@Special fun myFunction() { }
파라미터를 받는 경우, 다음과 같다. 그냥 일반 클래스와 크게 다르지 않게 느껴진다.
annotation class Review(val reviewer: String, val date: String)
@Review(reviewer = "John Doe", date = "2022-01-01")
class Document { }
기본적으로 빌트인 annotation들이 존재한다. @deprecated, @JvmOverloads, @JvmStatic, @Suppress, @Retention, @Target, @Repeatable 등. custom annotation에 대해선 사실, 별개의 포스팅으로 정리하는게 맞는 내용같고, 아래 참조글로 내용을 대체한다.
참조글 :
How to create custom annotations in Kotlin
Understanding Kotlin Annotations: A Comprehensive Guide

Generics, in, out : https://kotlinlang.org/docs/generics.html
Generics가 사용된건 오오오오래전 C++의 STL(Standard Template Library)등 Template 사용기억이 있기 때문에 이게 뭔지 자세한 설명은 생략. generic은 function, class, interface에 사용이 가능하다. 함수에서의 선언 방법은 다음과 같다.
fun <T> singletonList(item: T): List<T> {
}
fun <T> T.basicToString(): String { // extension function
}
val l = singletonList<Int>(1)
함수 이름전에 <T>로 써주며, 꼭 T가 아니어도 된다. 여러개인 경우, <T, F> 와 같이 써줄 수 있다. 여기서 T, F를 타입 파라미터라고 한다. 그리고 사용시에 이름뒤에 <>괄호와 함께 원하는 타입을 넣어주면 된다. 이 타입이 선언에 사용된 타입 파라미터 자리를 대치한다.
클래스의 경우는 다음과 같이 사용한다.
class Box<T>(t: T) {
var value = t
}
val box: Box<Int> = Box<Int>(1)
자바와 같이, 클래스 이름뒤에 <T> 와 같이 추가해준다. 이 후, 타입이 들어갈 자리에 이 T를 사용하게된다. 사용시에는 함수 때와 동일하게, 클래스 이름뒤에 <타입>과 같이 사용한다. 인터페이스도 클래스와 동일하게 사용한다.
이제 in, out 키워드에 대해 알아보자.
자바랑 차이점을 알아야 해서 일단 자바의 generic에 대해 좀 알아야 한다. 자바에서 generic type은 불변(invariant)이다. 무슨 말이냐면,
// Java
List<String> strs = new ArrayList<String>();
// Java reports a type mismatch here at compile-time.
List<Object> objs = strs;
위와 같이 List<String>과 List<Object>는 호환이 되지 않는다. 주어진 generic type만 사용이 가능하다. 이건 런타임시 안전을 위한 것이라고 한다. 하지만, 이런경우가 필요하지 않나? 다음 스택의 예를 보자.
//java
public void pushAll(Iterable<E> src) {
for (E e : src)
push(e);
...
Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ...;
// Error!
numberStack.pushAll (integers);
위와 같이 사용하면, invariant한 성질 때문에 Stack<Number>에 Integer를 넣을 수 없어서 에러가 발생한다. 이 때 자바에서 사용하는게 wildcard type 인자로 < ? extends E > 와 같이 사용한다.
// Java
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
이 것의 의미는 E 타입과 E의 서브타입을 다 받을 수 있다는 얘기로 위의 문제가 해결된다. 이렇게 wildcard와 extends-bound를 주게되면 이 타입은 covariant가 된다. 그럼 다음으로 popAll()을 생각해보자.
//java
public void popAll(Collection<E> dest) {
while(!isEmpty())
dest.add(pop());
}
...
Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ...;
numberStack.popAll(objects); // Error!!
- - -
public void popAll(Collection<? super E> dest) {
이 경우, Number를 Collection<Object>에 넣을 수 없기 때문에 또다시 에러에 직면한다. 이 때는 pushAll과 반대의 경우로, <? super E> wildcard type을 사용한다.
이 경우에는 contravariance라고 한다. 여기서 전자의 경우, 인스턴스를 생성해서 스택에 넣기 때문에 생산자(producer)라 칭하고, 후자의 경우 꺼내와 사용하기 때문에 소비자(consumer)라 칭한다. 그래서 이런경우 PECS(Producer-Extends, Consumer-Super)로 기억하고 사용한다.
그럼 이제 PECS에 해당하지 않는 경우에 대해 알아보자.
// Java
interface Source<T> {
T nextT();
}
// Java
void demo(Source<String> strs) {
Source<Object> objects = strs; // !!! Not allowed in Java
// ...
}
이 경우, consumer method도 없기 때문에 완전히 안전하고 막을 이유가 없다. 하지만, 이를 피하기위해 Source<? extends Obejct>를 써줘야 한다.
코틀린에서는 이를 컴파일러에게 알려줄 방법이 있다. declaration-site variance 라는 것이다. 타입 T가 오직 리턴만되고(produced), consumed 되는 일이 없다는 의미로 ‘out’을 써준다.
interface Source<out T> {
fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is OK, since T is an out-parameter
}
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
// Thus, you can assign x to a variable of type Comparable<Double>
val y: Comparable<Double> = x // OK!
}
이와 반대로 오직 소비(consumed)만 되고 생산(produced)은 안되는 경우에, ‘in’ 키워드를 사용한다. Comparable은 훌륭한 예이다.
이 외에도 type projection, star projection등이 있는데 문서를 참고하시길. 공식 문서 ( https://kotlinlang.org/docs/generics.html ) 나, 기타 다른 문서( 예: https://kt.academy/article/kfde-generics ) 등