일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- F
- Kotlin
- 싱글톤
- 코틀린
- 디자인패턴 #
- 디자인패턴
- 추상 팩토리
- Design Pattern
- 옵저버 패턴
- a
- r
- ㅓ
- 추상팩토리패턴
- 팩토리 메소드
- 빌터패턴
- Abstract Factory
- PrototypePattern
- Observer Pattern
- factory method
- Singleton
- builderPattern
- 함수형프로그래밍
- 프로토타입 패턴
- designPattern
- El
- Functional Programming
- ㅋㅁ
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
스타 프로젝션 본문
타입인자가 없을 표기하기 위해 <*>을 사용한다.
MutableList<*> 는 Mutable<Any?>와 같지 않다.
Mutable<Any?> - 모든 타입의 원소를 담을 수 있는 리스트.
MutableList<*> - 타입에 대한 정보가 정해지지 않은 리스트.
단, 타입이 정해지지 않았을 뿐이지 모든 타입을 담을수 있는것이 아니고 원소의 타입은 어찌 되었든 Any?(최상위)의 하위 타입이린간 분명하다.
fun main() {
val list: MutableList<Any?> = mutableListOf('a', 1, "qwe")
val chars = mutableListOf('a', 'b', 'c')
val unknownElements: MutableList<*> =
if(Random.nextBoolean()) list else chars
//unknownElements.add(42)
println(unknownElements)
}
여기서 unknownElements는 MutableList<out Any?> 처럼 동작한다.
unknownElements의 원소의 타입을 모를지라도 Any? 타입으로 원소를 꺼내올 수 있지만, 반대로 add는 사용할 수 없다.
타입파라미터를 시그니처에서 전혀 언급하지 않거나, 데이터를 그저 읽기만하고 타입에 관심이 없어 타입인자 정보가 중요하지 않을때 사용한다.
fun printFirst(list: List<*>){
if (list.isNotEmpty()){
println(list.first())
}
}
fun main() {
printFirst(listOf("kotlin", "java"))
}
스타프로젝션을 쓰는 쪽에서 더 간결하지만 제네릭 타입 파라미터가 결국 어떤 타입인지 굳이 알 필요가 없을 때만 스타 프로젝션을 사용할 수 있다.
스타프로젝션을 사용하는 방법과 실수를 알아보자
사용자 입력 검증기를 만들기.
interface FiledValidator<in T>{
fun validator(input: T): Boolean
}
object DefaultStringValidator: FiledValidator<String>{
override fun validator(input: String): Boolean = input.isNotEmpty()
}
object DefaultIntValidator: FiledValidator<Int>{
override fun validator(input: Int): Boolean = input >= 0
}
fun main() {
val validators = mutableMapOf<KClass<*>, FiledValidator<*>>()
validators[String::class] = DefaultStringValidator
validators[Int::class] = DefaultIntValidator
}
KClass는 코틀린 클래스를 표현한다.
FiledValidator<*>는 모든 검증기 타입을 표현한다. 그렇기 String을 검증하는데 있어 안전하지 못하다고 컴파일러는 판단한다.
즉, 알수 없는 타입의 검증기에 구체적인(String, Int)타입의 검증기를 넘기면 안전하지 못하단 뜻이다.
그렇다면 타입 캐스팅을 해보자.
위와 같이 안전하지 않은 캐스팅이란 경고를 보게된다.
fun main() {
val validators = mutableMapOf<KClass<*>, FiledValidator<*>>()
validators[String::class] = DefaultStringValidator
validators[Int::class] = DefaultIntValidator
val stringValidator = validators[Int::class] as FiledValidator<String>
println(stringValidator.validator(""))
}
Int검증기를 가져와서 String 검증으로 타입변환시 아무런 문제가 발생하지 않는다
하지만, 검증 메소드 내부에서 validator에서 오류가 발생한다.
실행시점에 모든 제네릭 타입 정보는 사라지기 때문에 타입캐스팅 자체는 문제가 없으나 메서드안에서 객체나 프로퍼티를 사용할경우 문제가 문제가 된다. 전적으로 개발자이 책임이 되버린다.
안전하지 못한 로직은 클래스 내부에 감추어 API를 만들었다. 외부에서 잘못된 사용을 하지 않도록 하였다.
object Validators {
private val validators =
mutableMapOf<KClass<*>, FiledValidator<*>>()
fun <T : Any> registerValidator(
kClass: KClass<T>, filedValidator: FiledValidator<T>
) {
validators[kClass] = filedValidator
}
@Suppress("UNCHECKED_CAST") // 경고무시.
operator fun <T : Any> get(kClass: KClass<T>): FiledValidator<T> =
validators[kClass] as? FiledValidator<T>
?: throw IllegalArgumentException("No validator for ${kClass.simpleName}")
}
fun main() {
Validators.registerValidator(String::class, DefaultStringValidator)
Validators.registerValidator(Int::class, DefaultIntValidator)
println(Validators[String::class].validate("kotlin"))
println(Validators[Int::class].validate(0))
}
제네릭 클래스의 타입 인자가 어떤 타입인지 정보가 없거나 타입인자가 어떤 타입인지 중요하지 않을때만 <*>을 사용해야된다.
'Kotlin in Action > 코틀린 답게 사용하기' 카테고리의 다른 글
애노테이션 만들기 (0) | 2021.03.05 |
---|---|
애노테이션과 리플렉션 (0) | 2021.03.04 |
사용 지점 변성: 타입이 언급되는 지점에서 변성지정 (0) | 2021.03.03 |
반공변성: 뒤집힌 하위 타입 관계 (0) | 2021.03.02 |
변성: 제네릭과 하위 타입2 (0) | 2021.03.02 |