Kotlin에서 제네릭 유형을 어떻게 확인할 수 있습니까?


84

Kotlin에서 제네릭 유형을 테스트하려고합니다.

if (value is Map<String, Any>) { ... }

그러나 컴파일러는 다음과 같이 불평합니다.

지워진 유형의 인스턴스를 확인할 수 없습니다 : jet.Map

일반 유형의 수표가 잘 작동합니다.

if (value is String) { ... }

Kotlin 0.4.68이 사용됩니다.

내가 여기서 무엇을 놓치고 있습니까?

답변:


92

문제는 유형 인수가 지워져 런타임에 해당 String 및 Any에 대한 정보가 없기 때문에 전체 유형 Map에 대해 확인할 수 없다는 것입니다.

이 문제를 해결하려면 와일드 카드를 사용하십시오.

if (value is Map<*, *>) {...}

큰! 완벽하게 작동합니다! 문서의 예제에서 혼란 스러웠습니다. confluence.jetbrains.net/display/Kotlin/Type+casts
— Philipp Brüll

51
실제로 무언가가 Collection<String>자동 캐스트로 만드는지 확인하려면 어떻게해야합니까?
— beruic

나는 이와 같은 스 니펫이 if (it.getSerializable(ARG_PARAMS) is HashMap<*, *>) {it.getSerializable(ARG_PARAMS) as HashMap<String, String>} else null있습니다. 그래서 기본적으로 제네릭 유형에 대해 확인 HashMap<String, Integer>하는 HashMap<String, String>경우 캐스팅 을 시도 합니다. 내가 뭔가를 놓치고 있습니까?
— Farid

@FARID 네, 것, 그리고 캐스팅의이 종류는 안전하지 않습니다
— 안드레이 Breslav

18

JVM은 일반 유형 정보를 제거합니다. 그러나 Kotlin은 제네릭을 수정했습니다. 제네릭 유형 T가있는 경우 인라인 함수의 유형 매개 변수 T를 수정 된 것으로 표시하여 런타임시 확인할 수 있습니다.

따라서 다음을 수행 할 수 있습니다.

inline fun <reified T> checkType(obj: Object, contract: T) {
  if (obj is T) {
    // object implements the contract type T
  }
}

3
전화하는 방법의 예를 보여줄 수 checkType()있습니까? 두 번째 인수에 대해 무엇을 전달해야할지 모르겠습니다.
— Michael Osofsky

10

이게 더 적절한 방법이라고 생각합니다

inline fun <reified T> tryCast(instance: Any?, block: T.() -> Unit) {
    if (instance is T) {
        block(instance)
    }
}

용법

// myVar is nullable
tryCast<MyType>(myVar) {
    // todo with this e.g.
    this.canDoSomething()
}

또 다른 짧은 접근법

inline fun <reified T> Any?.tryCast(block: T.() -> Unit) {
    if (this is T) {
        block()
    }
}

용법

// myVar is nullable
myVar.tryCast<MyType> {
    // todo with this e.g.
    this.canDoSomething()
}

1
이와 같은 것을 kotlin stdlib에서 직접 사용할 수없는 이유 :-(
— ATom

something as? String동일 하지 않습니까? 뒤에 물음표가 as있습니까?
— Dalibor Filus

@DaliborFilus 아니에요. 이것은 런타임 동안 Generics 및 지워진 유형에 관한 것입니다. Generics를 다룰 필요가 없다면 as?, correct를 사용할 수 있습니다 .
— stk

0

나는 위의 해결책을 시도해 보았고, tryCast<Array<String?>>많은 캐스팅이 포함 된 목록의 특정 작업에서 성능을 크게 저하 시켰기 때문에 그렇게 좋은 생각이 아니었다 고 생각합니다.

이것이 내가 마지막으로 한 해결책입니다. 다음과 같이 항목과 호출 방법을 수동으로 확인하십시오.

 fun foo() {
    val map: Map<String?, Any?> = mapOf()
    map.forEach { entry ->
        when (entry.value) {
            is String -> {
                doSomeWork(entry.key, entry.value as String)
            }
            is Array<*> -> {
                doSomeWork(entry.key, (entry.value as? Array<*>)?.map {
                    if (it is String) {
                        it
                    } else null
                }?.toList())
            }
        }
    }
}


private fun doSomeWork(key: String?, value: String) {

}
private fun doSomeWork(key: String?, values: List<String?>?) {

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.