Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- codility
- GOF
- spring scheduler
- 스프링 메일
- springboot
- Collections
- HashMap
- mybatis
- Spring
- 스프링부트
- Arrays
- pair
- maven
- 프로그래머스
- 스프링 부트
- Spring Mail
- pom.xml
- list
- @Scheduled
- thymeleaf
- Spring Boot
- java
- Dependency
- 프로젝트 구조
- 스프링
- C++
- vuejs #vue #js #프론트엔드 #nodejs #클라이언트사이드 #템플릿엔진
- 의존성관리
- 코딩테스트
- 스프링 스케줄러
Archives
- Today
- Total
Rooted In Develop
Kotlin - 제네릭스 본문
제네릭 함수와 프로퍼티
fun <T> List<T>.slice(indices: IntRange): List<T>
>>> println(letters.slice<Char>(0..2))
[a, b, c]
>>> println(letters.slice(10..13))
[k, l, m, n]
val authors = listOf("Dmitry", "Svetlana")
val readers = mutableListOf<String>(/* ... */)
fun <T> List<T>.filter(predicate: (t) -> Boolean): List<T>
>>> readers.filter { it !in authors }
val <T> List<T>.penultimate: T get() = this[size-2]
>>> println(listOf(1, 2, 3, 4).penultimate)
3
제네릭 클래스
interface List<T> {
operator fun get(index: Int) : T
}
class StringList: List<String> {
override fun get(index: Int) : String = /* ... */
}
class ArrayList<T>: List<T> {
override fun get(index: Int) : T = /* ... */
}
interface Comparable<T> {
fun compareTo(other: T) : Int
}
class String : Comparable<String> {
override fun compareTo(other: String) : Int = /* ... */
}
타입 파라미터 제약
fun <T : Number> List<T>.sum() : T
fun <T : Number> oneHalf(value: T) : Double {
return value.toDouble() / 2.0
}
fun <T: Comparable<T>> max(first: T, second: T) : T {
return if (first > second) first else second
}
fun <T> ensureTrailingPeriod(seq: T)
where T : charSequence, T : Appendable {
if (!seq.endsWith('.') {
seq.append('.')
}
}
타입 파라미터를 널이 될 수 없는 타입으로 제한
class Processor<T : Any> {
fun process(value: T) {
value.hashCode()
}
}
제네릭스 동작
실행 시점 : 타입 검사, 캐스트
if (value is List<String>) { ... } // ERROR
if (value is List<*>) { ... }
fun printSum(c: Collection<*>) {
val intList = c as? List<Int> // Int가 아닌 경우 ClassCastExcpetion
?: throw IllegalArgumentException("List is expected")
println(intList.sum())
}
fun printSum(c: Collection<Int>) {
if (c is List<Int>) {
println(c.sum())
}
}
실제화한 타입 파라미터를 사용
- 인라인 함수로 만들고 타입 파라미터를 reified로 지정
inline fun <reified T> isA(value: Any) = value is T
>>> println(isA<String>("abc"))
true
>>> println(isA(String>(123))
false
>>> val items = listOf("one", 2, "three")
>>> println(items.filterIsInstance<String>()) // 표준 라이브러리 함수 filterIsInstance
[one, three]
하위 타입 / 상위 타입
- 타입 A의 값이 필요한 모든 장소에 어떤 타입 B의 값을 넣어도 문제가 없는 경우 타입 B는 타입 A의 하위 타입
- A는 A?의 하위 타입이지만 A?는 A의 하위 타입이 아님
공변성 : 하위 타입 관계를 유지
- A가 B의 하위 타입일때 Producer<A>가 Producer<B>의 하위 타입이면 Producer는 공변적(하위 타입 관계가 유지된다)
- 제네릭 클래스가 타입 파라미터에 대해 공변적임으로 표시하려면 타입 파라미터 이름 앞에 out을 붙임
interface Producer<out T> {
fun produce() : T
}
open class Animal {
fun feed() { ... }
}
class Herd<T : Animal> {
val size: Int get() = ...
operator fun get(i: Int) : T { ... }
}
fun feedAll(animals: Herd<Animal>) {
for (i in 0 until animals.size) {
animals[i].feed()
}
}
class Cat : Animal() {
fun cleanLitter() { ... }
}
fun takeCareOfCats(cats: Herd<Cat>) {
for (i in 0 until cats.size) {
cats[i].cleanLitter()
}
// feedAll(cats) // Herd<Cat> <-> Herd<Animal> 간의 에러 발생
}
// 공변적 컬렉션 역할로 변경
class Herd<out T : Animal> {
}
fun takeCareOfCats(cats: Herd<Cat>) {
for (i in 0 until cats.size) {
cats[i].cleanLitter()
}
feedAll(cats)
}
- 공변적이기 위해서는 out 위치에만 T가 존재해야함
interface List<out T> : Collection<T> {
operator fun get(index: Int) : T // out 위치
}
interface List<out T> : Collection<T> {
fun subList(fromIndex: Int, toIndex: Int) : List<T> // out 위치
}
interface MutableList<T> : List<T>, MutableCollection<T> {
override fun add(element: T) : Boolean // in 위치이므로 MutableList는 T에 대해 공변적일 수 없음
}
- 읽기 전용 프로퍼티 : out
- 변경 가능 프로퍼티 : out, in
반공변성 : 뒤집힌 하위 타입 관계
interface Comparator<in T> {
fun compare(e1: T, e2: T) : Int { ... } // in 위치
}
공변성 | 반공변성 | 무공변성 |
Producer<out T> | Producer<in T> | MutableLisit<T> |
타입 인자의 하위 타입 관계가 유지됨 | 타입 인자의 하위 타입 관계가 뒤집힘 | 하위 타입 관계가 성립하지 않음 |
Producer<C>은 Producer<A>의 하위 타입 | Consumer<A>은 Consumer<C>의 하위 타입 | |
T를 out 위치에서만 사용 | T를 in 위치에서만 사용 | T를 아무 위치에서나 사용 |
사용 지점 변성 : 타입이 언급되는 지점에서 변성 지정
// out-projection 타입 파라미터
fun <T> copyData(source: MutableList<out T>, destination: MutableList<T>) {
for (item in source) {
destination.add(item)
}
}
>>> val list: MutableList<out Number> = ...
>>> list.add(42)
ERROR
// in-projection 타입 파라미터
fun <T> copyData(source: MutableList<T>, destination: MutableList<in T>) {
for (item in source) {
destination.add(item)
}
}
- Kotlin의 MutableList<out T> 는 Java의 MutableList<? extends T> 와 같음
- Kotlin의 MutableList<in T> 는 Java의 MutableList<? super T>와 같음
스타 프로젝션 : 타입 인자 대신 * 사용
- MutableList<*>는 MutableList<Any?>와 같지 않음
- MutableList<*>는 구체적인 특정 타입의 원소만 담을 수 있다는 의미
- 스타 프로젝션을 위한 검증기 구현
object Validators {
private val validators = mutableMapOf<KClass<*>, FieldValidator<*>>()
fun <T: Any> registerValidator(kClass: KClass<T>, fieldValidator: FieldValidator<T>) {
validators[kClass] = fieldValidator
}
@Suppress("UNCHECKED_CAST")
operator fun <T: Any> get(kClass: KClass<T>) : FieldValidator<T> =
validators[kClass] as? FieldValidator<T>
?: throw IllegalArgumentException("No validator for ${kClass.simpleName}")
}
>>> Validators.registerValidator(String::class, DefaultStringValidator)
>>> Validators.registerValidator(Int::calss, DefaultIntValidator)
>>> println(Validators[String::class].validate("Kotlin"))
true
>>> println(Validators[Int::class].validate(42))
true
'Back End > Kotlin' 카테고리의 다른 글
Kotlin - DSL (0) | 2023.06.24 |
---|---|
Kotlin - annotation, reflection (2) | 2023.06.17 |
Kotlin - 고차 함수 (0) | 2023.06.03 |
Kotlin - 연산자 오버로딩 (0) | 2023.05.27 |
Kotlin - 타입 시스템 (0) | 2023.05.06 |
Comments