// Developer, backend, frontend 코드 생략
object DeveloperPool {
fun add(developer: Developer) = when(developer) {
is backend -> pool[developer.name] = developer
is frontend -> pool[developer.name] = developer
//else -> {
// println("지원하지 않는 개발자입니다.")
//}
fun get(name: String) = pool[name]
}
fun main() {
val backend = Backend(name="토니")
DeveloperPool.add(backend)
val frontend = Frontend(name="엘리")
DeveloperPool.add(frontend)
println(DeveloperPool.get("토니"))
println(DeveloperPool.get("엘리"))
}
코틀린은 클래스에 상속하거나 디자인 패턴을 사용하지 않고 새로운 기능으로 클래스를 확장 할 수 있는 기능을 제공하는데 이것이 확장(extension)이라는 선언을 통해 이루어진다.
이때 추가적인 메소드를 구현하면 이를 "확장 함수"라고 하고 추가적인 프로퍼티를 구현하면 "확장 프로퍼티"라고 한다.
주의할 점은 기존에 존재하는 맴버함수와 동일한 이름(+동일한 시그니처)의 확장함수가 있을 경우에는 컴파일 오류가 발생하지 않고, 맴버함수가 우선적으로 호출되므로 확장 함수는 실행되지 않는다. 흔치않지만 본의아니게 코틀린 라이브러리에 쓰이는 함수명을 사용할 경우에 문제가 생길 수 있다.
fun String.first() : Char {
return this[0];
}
fun String.addfirst(char: Char) : String {
return char + this.substring(0)
}
fun main() {
println("ABCD".first()) //결과 A
println("ABCD".addFirst('Z')) //결과 ZABCD
}
<>안에 type parameter를 추가하고 ()에 타입 파라미터를 받는 변수를 추가한 클래스를 만들고,
제네릭으로 만든 클래스의 인스턴스는 type argument를 제공해주면 된다.
class MyGenerics<T>(val t: T) {
}
fun main() {
// 제네릭을 사용한 클래스의 인스턴스를 만들려면 타입아규먼트를 제공
// val generics = MyGenerics<String>("테스트")
//타입생략가능
val generics = MyGenerics("테스트")
//변수의 타입에 제네릭을 사용한 경우
val list1: MutableList<String> = mutableListOf()
// 타입아규먼트를 생성자에서 추가
val list2 = mutableListOf<String>()
// 정확한 타입을 모르는 경우에도 제네릭을 안전하게 사용가능
val list3 : List<*> = list<String>("테스트")
val list4 : List<*> = list<Int>(1,2,3,4)
}
제네릭에서 파라미터화 된 타입이 어떤 관계에 있는지 설명하는 개념을 변성이라고 한다.
변성은 공변성, 반공변성, 무공변성 세종류가 있다.
공변성은 자바 제네릭의 Extends, 코틀린에선 out,
반공변성은 자바 제네릭의 Super, 코틀린에선 in을
서로 관계가 없으면 무공변성으로 아무것도 쓰지 않는다.
class MyGenerics<out T>(val t: T){
}
지연초기화는 대상에 대한 초기화를 미뤘다가 실제 사용시점에 초기화하는 기법으로 초기화 과정에서 자원이 많이 쓰이거나 오버헤드가 발생할 경우 지연초기화를 사용하는 것이 유리할 수 있다.
많이 쓰이는 예시로는 웹페이지에서 특정 스크롤에 도달했을때 컨텐츠를 보여주는 무한 스크롤이나
싱글톤 패턴에서 LazyHolder방식의 지연초기화, JPA의 엔티티를 사용하는 시점에 로딩하는 LazyLoading기능이 있다.
코틀린에선 늦은 초기화, 초기화 지연을 할 수 있는 lateinit과 lazy 프로퍼티를 제공한다.
by lazy를 사용하면 불변변수를 유지하면서 코드를 사용하는 시점에 1회 초기화를 할 수 있으며 티 쓰레드 환경에서도 안전하게 동작하게 설계되어 있다.
만약 초기화를 여러번 수행하고 싶거나 동기화를 끄고싶다면 by lazy(LazyThreadSafetyMode.NONE)처럼 설정을 바꾸어 사용할 수도 있다.
lateinit는 가변프로퍼티에 대하여 지연초기화가 필요한 경우에 사용한다.
예를들어 스프링에 @Authwired를 사용하여 DI를 적용할 때나 @Setup으로 초기화할때 쓰인다.