Unsafe smart casts for open or custom getter


#1

As we all know, smart cast is something like this:

fun main(args: Array<String>) {
	val a: Any = 233
	// a + 1 // error
	if (a is Int) println(a + 1) // OK
}

It also works with properties of objects.

But It seems not works with open/custom getters:

interface A { val a: Any }
class B(override val a: Any) : A
fun main(ags: Array<String>) {
  val a: A = B(2333)
  if (a.a is Int) print(a.a + 1) // error
}

I guess the reason is: the getter maybe returns different value in different times. So it cannot be smart cast.

But I think their can be something like ‘unsafe’: which the compiler ignore these kinds of issues.

Also, ‘unsafe’ will work with nullable variables:

var foo: SomeClass? = something
if (foo == null) return
foo.bar // here foo.bar is smartly cast as NotNull

How about that?

Sorry for my English.


#2

If you call a get, the value can change in the class. Example

class B {
  val a
      get() : Any {
        if (random() % 2 == 0) 
          return  "1"
       else
          return 1.0
    }
}

If you save the value in a var, the smart check works

interface A { val a: Any }
class B(override val a: Any) : A
fun main(ags: Array<String>) {
  val a: A = B(2333)
  val aa = a.a
  if (aa is Int) print(aa + 1) // work
}

#3

I know exactly the issue, but sometimes they’re logically impossible to change. what I want is to tell the compiler that do not check this problem. It can be successfully compiled as unsafe code.


#4

We have no plans to add such an “unsafe” feature to the language. If you want to write unsafe code, you can use !! to convert any value from a nullable to a non-null type.


#6
interface A { val a: Any }
class B(override val a: Any) : A
fun main(ags: Array<String>) {
  val a: A = B(2333)
  val aa = a.a
  if (aa is Int) print(aa + 1) // work
}

In situations like this where I need to capture a value from a property and use the value multiple times I often use the let method:

fun main(ags: Array<String>) {
  val a: A = B(2333)
  a.a.let { if (it is Int) print(it + 1) }
}

or sometimes use multiple calls in a fluent API (note that takeIf is only in 1.1.0 and above):

fun main(ags: Array<String>) {
  val a: A = B(2333)
  a.a
      .takeIf { it is Int }
      ?.let { it + 1 }
      ?.let { print(it) }
}

Those calls are all inlined so are equivalent to first version.