Hanbit the Developer
Kotlin Documentation | Generics: in, out, where 본문
Category: Concepts - Classes and objects
문서 링크: https://kotlinlang.org/docs/generics.html
Variance: in, out
배경: String은 Object의 하위 타입이지만, MutableList<String>은 MutableList<Object>의 서브클래스가 아니기 때문에 기존의 generic을 통한 객체지향 프로그래밍 설계가 힘듦
Example
List는 covariance(public interface List<out E>)이기 때문에 정상 작동함
open class Animal
class Cat : Animal()
class Dog : Animal()
fun myAnimals(animals: List<Animal>) {
println(animals[0])
}
fun main() {
val cats: List<Cat> = listOf(Cat(), Cat())
myAnimals(cats)
}
반면, MutableList는 invariant(using normal generic)이기 때문에 컴파일 오류가 발생함
open class Animal
class Cat : Animal()
class Dog : Animal()
fun myAnimals(animals: MutableList<Animal>) {
println(animals[0])
}
fun main() {
val cats: MutableList<Cat> = mutableListOf(Cat(), Cat())
myAnimals(cats)
}
Star-projections
type을 모르고 있으나 안전하게 사용하기 위해 사용된다. Any와 달리, 한 번 타입이 결정되면 다른 타입을 사용할 수 없다.
Function<*, String>
where
upper bound를 여러 개 거는 경우에 사용된다.
fun <T : Comparable<T>> sort(list: List<T>) { ... }
위 함수에서 Comparable<T>는 T로 올 수 있는 타입의 upper bound를 정의한다. 이때 upper bound를 하나 더 걸고 싶다면 아래와 같이 작성한다:
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
Type erasure: is, as
if (something is List<*>) {
something.forEach { println(it) } // The items are typed as `Any?`
}
inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
if (first !is A || second !is B) return null
return first as A to second as B
}
val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)
val stringToSomething = somePair.asPairOf<String, Any>()
val stringToInt = somePair.asPairOf<String, Int>()
val stringToList = somePair.asPairOf<String, List<*>>()
val stringToStringList = somePair.asPairOf<String, List<String>>() // Compiles but breaks type safety!
// Expand the sample for more details
Underscore operator
다른 타입에 의해 타입을 추론할 수 있는 경우 언더스코어를 통해 자동으로 추론하게 만들 수 있다:
abstract class SomeClass<T> {
abstract fun execute() : T
}
class SomeImplementation : SomeClass<String>() {
override fun execute(): String = "Test"
}
object Runner {
inline fun <reified S: SomeClass<T>, T> run() : T {
return S::class.java.getDeclaredConstructor().newInstance().execute()
}
}
fun main() {
// T is inferred as String because SomeImplementation derives from SomeClass<String>
val s = Runner.run<SomeImplementation, _>()
assert(s == "Test")
}
References
https://readystory.tistory.com/201
'Kotlin' 카테고리의 다른 글
Kotlin Documentation | Enum classes (0) | 2023.05.23 |
---|---|
Kotlin Documentation | Nested and inner classes (0) | 2023.05.23 |
Kotlin Documentation | Sealed classes and interfaces (0) | 2023.05.23 |
Kotlin Documentation | Data classes (0) | 2023.05.23 |
Kotlin Documentation | Extensions (0) | 2023.05.22 |