Compiler failed to see null check?


#1

Hi,

Is there a reason the following program will not compile? It failed on last line with “Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Resource?”. But as you can see I have a if check for null right before it.

class Resource(val name : String) {
     fun close() = println("Clean up $name.")
}
fun createResource(n : Int) : Resource {
     if ( n % 2 == 0)
          throw IllegalArgumentException("We can't except even number.")
     return Resource("Cool $n")
}
fun main(args : Array<String>) {
     var resource : Resource? = null
     try {
          resource = createResource(0)
     } catch (e : Exception) {
          e.printStackTrace()
     } finally {
          if (resource != null)
               resource.close() // Failed here unless I invoke with ?. or !!
     }
}

Thanks,
Zemian


#2

The compiler can't guarantee that resource is still null. It is a mutable variable so it might have been set to null by another thread after the null check. In this case that's obviously not possible because it's a local variable but the compiler can't figure that out (yet?).

Does anyone know if Kotlin has something like try-with-resources (or if something is planned)? Initialising a mutable variable to null, reassigning it in the try block and checking its value in the finally block is such an ugly pattern.


#3

In kotlin.io package

/** Uses the given resource then closes it down correctly whether an exception is thrown or not */
public inline fun <T: Closeable, R> T.use(block: (T)-> R) : R

https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/io/JIO.kt#L147

Sample, also from JIO.kt

class Resource(val name : String) {
     fun close() = println("Clean up $name.")
}
fun createResource(n : Int) : Resource {
     if ( n % 2 == 0)
          throw IllegalArgumentException("We can't except even number.")
     return Resource("Cool $n")
}
fun main(args : Array<String>) {
     var resource : Resource? = null
     try {
          resource = createResource(0)
     } catch (e : Exception) {
          e.printStackTrace()
     } finally {
          if (resource != null)
               resource.close() // Failed here unless I invoke with ?. or !!
     }
}

#4

Thank you guys for the notes. I am aware the Kolin has the io package and can do this better than my sample code. However the point of the post is that Kotlin compiler shouldn't need to force user to use "!!" when we just had a null check on it for local variable. At least that's how I undertand how Kotlin has claimed able to do. Did I miss something here or we consider this as a bug?


#5

Smart casts for local vars is a feature which is not implemented yet, see KT-3175.