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.