(object) is (type)
(object) !is (type)
is연산자와 부정형인 !is연산자는 주어진 객체가 주어진 타입에 속하는지 여부를 나타내는 값을 반환합니다.
val num = 5
println(num is Int) // 출력: true
대부분의 경우, 코틀린에서는 immutable한 값에 대해 명시적 형변환을 직접 해줄 필요가 없습니다. 컴파일러가 is 타입검사를 추적하고 형변환이 필요하면 자동으로 안전한 형변환을 삽입해주기 때문입니다.
fun smartCast(arg: Any?) {
arg?:return
if (arg is String) // is checked
println("$arg.length is ${arg.length}") // arg is automatically cast to String
else if (arg is Int)
println("${arg + 4}") // arg is automatically cast to Int
}
fun main() {
smartCast(5)
smartCast("abc")
smartCast(null)
}
컴파일러는 !is 연산도 추적하여 스마트 컴파일을 실행합니다.
fun smartCast(arg: Any?) {
arg?:return
if (arg !is String) // !is checked
return
else
println("$arg.length is ${arg.length}") // arg is automatically cast to String
}
fun main() {
smartCast("abc")
smartCast(null)
}
또는 &&, ||과 같은 논리연산에서 이뤄진 is 연산도 추적할 수 있습니다.
fun smartCast(arg: Any?) {
arg?:return
if (arg !is String || arg.length == 0) // !is checked
return
if (arg is String && arg.length > 0)
println("$arg.length is ${arg.length}") // arg is automatically cast to String
}
fun main() {
smartCast("abc")
smartCast("")
}
스마트 캐스트는 when과 while에서도 적용됩니다.
fun smartCastWithWhen(arg: Any?) {
arg?:return
when (arg) {
is Int -> println(arg + 1) // smartcast
is String -> println(arg.length + 1) // smartcast
is IntArray -> println(arg.sum()) // smartcast
}
}
fun smartCastWithWhile(arg: Any?) {
var idx = 0
while (arg is IntArray && idx < arg.size) {
print(arg[idx])
idx++
}
}
fun main() {
smartCast("abc")
smartCast(10)
smartCast(intArrayOf(1, 2, 3))
}
[출력]
4
11
6
1234
스마트 캐스트는 is 검사와 변수의 사용 사이에 변수가 변하지 않는다고 컴파일러가 확신할때만 실행됩니다. 보다 구체적인 조건은 아래와 같은 상황들입니다.
val 지역변수 : 로컬 위임 프로퍼티(local deligated property)를 제외하고 항상 가능합니다.
val 프로퍼티 : 프로퍼티가 private 또는 internal이거나 속성이 선언된 모듈 내에서 검사되는 경우엔 스마트 캐스트를 사용할 수 있습니다. 하지만 프로퍼티가 open이거나 사용자 정의 게터를 가지고 있는 경우에는 사용할 수 없습니다.
var 지역변수 : 변수가 검사와 사용 사이에 변경되지 않을때, 그 변수가 람다함수에서 수정되지 않을때, 로컬 위임 프로퍼티(local deligated property)가 아닐 때 스마트 캐스트를 사용할 수 있습니다.
var 프로퍼티 : 다른 코드에 의해 언제든 수정될 수 있기때문에 스마트 캐스트를 사용할 수 없습니다.
일반적으로 캐스트 연산자는 캐스트가 가능하지 않은 경우 예외를 throw합니다. 그래서 안전하지 않다고 합니다. Kotlin의 안전하지 않은 캐스트는 중위 연산자 as에 의해 수행됩니다.
val x: String = y as String
변환하려는 타입이 nullable이 아니므로 null을 String으로 캐스팅할 수 없습니다. y가 null이면 위의 코드에서 예외가 발생합니다. null 값에 대해 이와 같은 코드를 올바르게 만들려면 캐스트의 오른쪽에 nullable 형식을 사용하십시오.
val x: String? = y as String?
예외가 throw 되는 상황을 피하려면 안전한 캐스트 연산자인 as?을 사용해야 합니다. as?은 캐스트가 실패하면 null을 반환합니다.
val y = 5
val x: String? = y as? String
println(x) // 출력: null
코틀린은 컴파일 타임에 제네릭을 포함하는 연산에 대한 안전을 보장합니다. 반면 런타임에 제네릭 타입의 인스턴스는 그들의 실제 타입에 대한 정보를 가지고 있지 않습니다. 예를 들어, List<foo>는 List<*>처럼 타입 정보가 지워집니다. 일반적으로, 인스턴스가 런타임에 특정 형식 인수를 가진 제네릭 형식에 속하는지 확인할 수 있는 방법은 없습니다.
이 때문에 컴파일러는 ints is List<Int> 또는 list is T(유형 매개변수)와 같이 타입 삭제로 인해 런타임에 수행할 수 없는 is-checks를 금지합니다. 그러나 * 투영 유형를 사용하여 is-checks를 수행할 수 있습니다.
fun checkStarProjectedType(arg: Any?) {
arg ?: return
if (arg is List<*>) {
arg.forEach { println(it) } // The items are typed as `Any?`
}
}
fun main() {
val intLst = listOf(1, 2, 3)
val strLst = listOf("abc", "cde", "fgh")
checkStarProjectedType(intLst)
checkStarProjectedType(strLst)
}
[출력]
1
2
3
abc
cde
fgh
인스턴스가 제네릭의 인자로 타입이 명시된 경우, 제네릭 타입을 제외한 구체적으로 구현된 객체에 대한 is-check는 가능합니다. 이 경우 제네릭 표현(ex.<Int>)은 생략 될 수 있습니다.
fun smartcastWithStaticGeneric(list: List<Int>?) {
// List<int> -> 제네릭의 타입이 명시되어 있음
list ?: return
if (list is ArrayList) {
// 따라서 제네릭을 제외한 객체인 ArrayList에 대해 is-check가능
// ArrayList<String..etc>는 불가능
list.forEach { println(it) }
// `list` is smart-cast to `ArrayList<String>`
} else {
println("this is not ArrayList")
}
}
fun main() {
val intLst = listOf(1, 2, 3)
smartcastWithStaticGeneric(intLst)
}
reified type parameters를 갖는 인라인 함수는 각 호출되는 장소에서 인라인 된 실제 인수를 가지고 있습니다. 이것은 타입 파라미터에 대한 arg is T 검사를 가능하게 만듭니다. 하지만 만약 arg가 제네릭 타입 그 자체라면, arg의 타입은 여전히 지워져있습니다.
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
추가 예정...