kotlin 2.0.0에서 스마트캐스트가 더 똑똑해졌다고 한다. 한번 살펴보자
class Cat {
fun purr() {
println("Purr purr")
}
}
// 기존의 방법
fun petAnimal(animal: Any) {
if (animal is Cat) {
animal.purr()
}
}
fun petAnimal(animal: Any) {
val isCat = animal is Cat // 타입 체크 변수로 선언
if (isCat) {
animal.purr() // 2.0 이전 버전에선 타입 캐스트가 되지 않아 호출할 수 없다!
}
}
fun main() {
val kitty = Cat()
petAnimal(kitty)
}
타입 체크를 지역 변수로 선언하면 if 조건문 밖이라도 해당 변수에 접근하여 자동으로 형변환을 해줄 수 있다.
interface Status {
fun signal() {}
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status
fun signalCheck(signalStatus: Any) {
if (signalStatus is Postponed || signalStatus is Declined) {
// signalStatus는 공통 supertype인 Status로 스마트캐스트된다
signalStatus.signal()
// 2.0.0 이전 버전에서는 Any 타입으로 변환된다
if(signalStatus is Status) signalStatus.signal() // 그래서 타입 체크 후에 signal() 호출해야 함
}
}
interface Processor {
fun process()
}
inline fun inlineAction(f: () -> Unit) = f()
fun nextProcessor(): Processor? = null
fun runProcessor(): Processor? {
var processor: Processor? = null
inlineAction {
// kotlin 2.0.0에서는 컴파일러가 processor 변수와 inlineAction()이 각각 지역변수와 inline fun인것을 인지하고 있다.
// 따라서 heap에 객체가 생성되지 않기 때문에 외부로 노출이 안된다는 것을 인지할 수 있다
// 그런 이유로 processor가 null이 아니면 스마트캐스트 해버린다
if (processor != null) {
processor.process()
processor?.process() // kotlin 2.0.0 이전 버전에서는 safe call이 필요하다
}
processor = nextProcessor()
}
return processor
}
class Holder(val provider: (() -> Unit)?) {
fun process() {
// kotlin 2.0.0.에서 provider가 null이 아니라면 스마트캐스트된다
if (provider != null) {
provider()
// kotlin 2.0.0 이전 버전에서는 다음과 같이 safe call이 필요하다
provider?.invoke()
}
}
}
fun testString() {
var stringInput: String? = null
stringInput = "" // 이제부터 stringInput는 String 타입으로 스마트캐스트된다
try {
// 컴파일러가 stringInput이 null이 아닌 것을 안다
println(stringInput.length)
stringInput = null // 컴파일러가 이전의 스마트캐스트를 거절하고 다시 nullable한 String? 타입으로 취급한다
// 예외 발생, 현재 String? 타입이다
if (2 > 1) throw Exception()
stringInput = ""
} catch (exception: Exception) {
// kotlin 2.0.0에서는 stringInput을 String?으로 인식하기 때문에 safe call이 필요하다
println(stringInput?.length)
// kotlin 2.0.0 이전 버전에서는 safe call이 필요하지 않지만 이것은 잘못됐다는 것을 우리는 알고 있다
}
}
interface Rho {
operator fun inc(): Sigma = TODO()
}
interface Sigma : Rho {
fun sigma() = Unit
}
interface Tau {
fun tau() = Unit
}
fun main(input: Rho) {
var unknownObject: Rho = input
if (unknownObject is Tau) {
// inc() 연산자를 통해 Sigma로 스마트캐스트된다
++unknownObject
// kotlin 2.0.0버전의 컴파일러는 unknownObject가 Sigma 타입이란 것을 알기 때문에 sigma() 호출이 가능하다
unknownObject.sigma()
// kotlin 2.0.0 이전 버전에서는 증감 연산자를 통해서 스마트캐스트를 해주지 않기 때문에 sigma()를 호출할 수 없다.
// In Kotlin 2.0.0에서는 unknownObject가 Sigma로 스마트캐스트되어서 tau()를 호출할 수 없다
// 하지만 2.0.0 이전 버전에서는 증감연산자를 통해 스마트캐스트가 되지 않아 Tau타입이 유지되어 호출이 가능하다
unknownObject.tau()
}
}