Kotlin Contracts not working for null-check in extension function - kotlin

I'm trying to write an extension function that returns true if the value is not null or 0 and use a contract to guarantee to the compiler that if I return true, the value is non-null. However, it doesn't seem to be working with smart casting. It still won't compile when I try to pass the value into a function that takes a non-nullable Long.
I tried to compile this code and it would not work. I expected the id to be smart-casted to a Long from a Long? since the contract guarantees that if isValidId returns true then the passed in Long? is not null.
As you can see, the property is immutable, so I don't think that's the issue. I also added some more code below, because the problem appears to be specific to extension functions. It works when I pass ID as a traditional parameter.
fun test() {
val id: Long? = null //5L
if (id.isValidID()) {
// It won't compile because the compiler says that id is a Long?
// instead of smart casting it to a Long. doWithId requires a Long.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun Long?.isValidID(): Boolean {
contract { returns(true) implies (this#isValidID != null) }
return this != null && this != 0L
}
Thanks!
EDIT:
Oh! It works when it's not an extension function. Does anybody know how I can make extension functions work?
fun test() {
val id: Long? = null
if (isValidID(id)) {
// This compiles now that I pass ID as a param instead of
// passing it in an extension function.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun isValidID(id: Long?): Boolean {
contract { returns(true) implies (id != null) }
return id != null && id != 0L
}

Related

kotlin: If value is null then exit, else use that value as not nullable

Essentially this is in the title. I have a value that could be null. If it is, I just want to exit with a message. If it's not null, then there's a whole slew of work I need to do with this value.
I've found similar, but not quite this exact situation. And it's the subtle difference that's driving me nuts. Here is my code in java:
if (value == null) {
print("error!");
return;
}
print(value);
doFunStuff(value);
etc(value);
All those methods using value require it to be non-null.
But I'm having a difficult time figuring this out in kotlin. With everything I try, the compiler still insists that value is still nullable and refuses to use it in the functions.
What is the kotlin way of doing this very common code flow?
If your methods truly have non-null parameters, the Kotlin compiler should be smart enough to do a smart cast to Object from Object?.
fun yourMethod(value: Object?) {
if (value == null) {
print("error!")
return
}
print(value) // Smart cast happens here
doFunStuff(value)
etc(value)
}
fun print(value: Object) {
// Implementation
}
fun doFunStuff(value: Object) {
// Implementation
}
fun etc(value: Object) {
// Implementation
}
But you can also force the conversion by using the !! operator (though in this case the compiler will tell you it's not necessary):
fun yourMethod(value: Object?) {
if (value == null) {
print("error!")
return
}
val nonNullValue = value!!
print(nonNullValue)
doFunStuff(nonNullValue)
etc(nonNullValue)
}
fun print(value: Object) {
// Implementation
}
fun doFunStuff(value: Object) {
// Implementation
}
fun etc(value: Object) {
// Implementation
}
If your value is a local variable or a function parameter, you won't have this problem, because the compiler will smart-cast it to not-null.
So, I'm assuming value in this case is a member property.
Option 1 is to copy it to a local variable to use in the function:
val value = value
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
Option 2 is to use the let or also scope functions to do the same thing, but this might not be a good option here because so much code would become nested. This is more useful when you're only calling one or two functions with the object, in which case, you wouldn't even have to name it (just call it it).
value.let { value ->
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
}
If your entire function works with this one property, you can avoid the nesting problem like this, if you don't mind it returning something besides Unit:
fun foo() = value.also { value ->
if (value == null) {
print("error!")
return
}
print(value)
doFunStuff(value)
etc(value)
}
Option 3 is to assert non-null every time you use it, but this is very ugly. This is only safe if you know the property is only ever accessed from the same thread this function is ever called on.
if (value == null) {
print("error!")
return
}
print(value!!)
doFunStuff(value!!)
etc(value!!)
Expanding on #Mehul's answer, this would only run the code in the let if the value was not null. If null, you could run the outside process and return from it.
value?.let { nonNullValue ->
print(nonNullValue);
doFunStuff(nonNullValue);
etc(nonNullValue);
}?: run { print("error!") ; return }
That said, since you are no longer needing the return to abort the function if null, you could simply do this and further clean it up replacing the lambda.
value?.let {
print(it);
doFunStuff(it);
etc(it);
}?: print("error!")
Well, have you already tried something like this and this is not what you expect?
value?.let { nonNullValue ->
print(nonNullValue);
doFunStuff(nonNullValue);
etc(nonNullValue);
}
basically the code inside let block will run only if the value isn't null.

How can I let the Kotlin compiler know I already checked for null inside a function?

Basically I have a function that does some stuff but specifically it checks if two values I'm passing are null. This function is called from various places to make sure those two values are not null. Kotlin is complaining later that I'm not checking for null. Is there a way I can do this so that Kotlin already knows that I am checking for null without using !!?
Here is a simple example:
private fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
private fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
private fun testNull(possibleNull: String?): Boolean {
return possibleNull != null
}
Basically testNull is only true if possibleNull is not null and that check is on an if right before calling mustPassNonNull so can I let Kotlin know I'm already checking that? without using !! of course.
Thanks.
It is possible with the use of contracts. Currently in experimental in Kotlin 1.3.
It is possible to declare contracts for your own functions, but this feature is experimental, as the current syntax is in a state of early prototype and will most probably be changed. Also, please note, that currently the Kotlin compiler does not verify contracts, so it's a programmer's responsibility to write correct and sound contracts. -kotlinlang.org
#ExperimentalContracts
fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
#ExperimentalContracts
fun testNull(possibleNull: String?): Boolean {
contract{
returns(true) implies (possibleNull is String)
}
return possibleNull != null
}
Articles I referenced:
https://kotlinlang.org/docs/reference/whatsnew13.html
https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2
It seems like a simple let situation
private fun stuff() {
var possibleNull: String? = "test"
possibleNull?.let { mustPassNonNull(it) }
}
This way mustPassNonNull will know that it isn't null :)
Also, if you need to do more than just check for nullability you could do:
possibleNull
?.takeIf { /* it's not null here anymore, add any checks you need */}
?.let { /* both non-null and checked for whatever you need */}

Kotlin: Null check in if block

I started to learn Kotlin recently. One thing I can't get my head around is the null check blocks. The following statement is considered unsafe, and the compiler won't allow you to compile this.
var testVar: String? = null;
// if (testVar != null )
{
// Doing some work....
println(testVar.length)
}
But when you uncomment the if line, everything works. This seems great.
But what if // Doing some work.... is computationally expensive and another thread changes the value of testVar to null while this thread is in // Doing some work line? In this scenario:
Does the program throw a NullPointerException?
OR:
Does the bytecode cache the value of testVar and use the cached value inside the if block?
In your initial example, is your var local to a function? For example:
fun doStuff() {
var testVar: String? = null
if (testVar != null) {
println(testVar.length)
}
}
In this case, no other scope has reference to testVar so nothing else can change it. However, if the var is a class property, i.e.
class MyClass {
var testVar: String? = null
fun doStuff() {
if (testVar != null) {
println(testVar.length)
}
}
}
This will fail to compile as testVar could have been set to null by another thread in between the check and the usage.
Going further, if you try to be tricky:
fun doStuff() {
var testVar: String? = null
fun trickyFunction() {
testVar = null
}
if (testVar != null) {
trickyFunction()
println(testVar.length)
}
}
The compiler will fail as your variable is captured by a changing closure. So in general, if you are able to use the variable via a smart-cast to non-null, you should not have to worry about any potential for NPEs.
For the second scenario (var properties) it is preferable to rely on .let to capture an immutable reference to the current value, i.e.
testVar?.let { capturedTestVar ->
println(capturedTestVar.length)
}

Kotlin 1.3.11 has broken null-safety?

fun handle() : String {
null?.let { return "Ololo"}
}
val result = handle()
result.trim() // kotlin.TypeCastException: null cannot be cast to non-null type kotlin.CharSequence
Any ideas why null-safe Kotlin function return null?
It's a bug caused by introducing contracts for the standard functions let, run, apply, also in Kotlin 1.3.
The fix is targeted to the version 1.3.20. See KT-28061 for details.
It looks like the Kotlin compiler is adding in a null return in case let doesn't execute. This is probably a bug, since it shouldn't compile, and doesn't in previous versions of Kotlin.
If we just compile your example, we get this:
#NotNull
public final String handle() {
return null;
}
I think that's just a compiler optimization, since null?.let() will never execute.
Using an actual variable yields:
#NotNull
public final String handle() {
return someNullableVariable != null ? "Ololo" : null;
}
In other words, let() doesn't execute if its reference is null. However, since this function needs to return something, the compiler just tells it to return null, since there's nothing else it could return.
Since the function is marked #NotNull, Kotlin will perform a null-check on anything referencing the function:
fun someOtherMethod() {
handle().trim()
}
Becomes
public final void someOtherMethod() {
String handle = handle();
if (handle != null) {
StringsKt__StringsKt.trim(handle).toString();
return;
}
throw new Exception("null cannot be cast to non-null type kotlin.CharSequence");
}
There are two ways to handle this. You could change the return type on handle() to String?:
fun handle(): String? {
someNullableVariable?.let { return "Ololo" }
}
Or you could return something else in case the variable is null:
fun handle(): String {
someNullableVariable?.let { return "Ololo" }
return "someNullableVariable was null"
}
It has to be a bug, because:
a return statement (or better: expression) is missing, since the lambda passed to let won't be invoked
a function with String as return type should never return null.
Interestingly, in Kotlin 1.2.x this does not even compile:
fun handle() : String {
null?.let { return "Ololo"}
}
Error:(6, 0) A 'return' expression required in a function with a block body ('{...}')
In Kotlin 1.3.11 it does.
In any case:
let won't be invoked, because the safe-call operator ? evaluates to null (in this case).

Kotlin - Why do we have to explicit type parameter(s) for generic method?

I'm working on extension method like this:
infix fun <T> T.isNullOr(other: T): Boolean {
if (this == null) return true
return this == other
}
and I'm trying to use this method like this.
val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?
I expected compilation error because type parameter is automatically set to Boolean for isNullOr but it wasn't. What's happening?
am I misunderstanding about it?
in C#, same code working well as I expected.
static bool IsNullOr<T>(this T t, T other) {
if (t == null) return true;
return Equals(t, other);
}
bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this
Here, val thisShouldNotWork = true isNullOr 0 is equal to val thisShouldNotWork: Boolean = true.isNullOr<Any>(0). Type parameter as inferred as the closest parent.
And function's return type is based on logical expression evaluation: this == other. Let's see == function declaration: public open operator fun equals(other: Any?): Boolean. It receives Any?.
Type parameter in this function has nothing to do with Boolean.
Just remember that generic type information is erased at runtime and whenever you try to put something into a method that accepts generics, then the common denominator is assumed, e.g.:
listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>
Now for your example that would mean "one".isNullOr(123) both become Any.
As a sidenote however, if you declare a specific type (e.g. List<String>) as shown next, it will not work to assign a different type to it:
val test : List<String> = listOf(123) // this will not work
It is already known at compile time that the given int can't become a string. This sample however doesn't help you as you do not return that generic type. If your method just looked a bit different, e.g. would have a generic type as return value, it might easily have worked out similar to the List-sample before.
So to fix your sample you need to specify the type which will basically make the infix obsolete, e.g. the following will work as you expect:
val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)
Note that for what you've shown some standard functionality might help you (but not eliminate that specific problem), i.e. using the ?: (elvis operator) with a ?.let:
val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let {
it == "some other string to compare"
} ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
it == 123 // compile error (funny enough: it.equals(123) would work ;-)
} ?: true /* it is null */
I think in this case the generics don't really matter. You only call equals in the method, which you can do on any type. It's basically the same as:
infix fun Any.isNullOr(other: Any): Boolean {
return this == other
}
It compiles without problems because you can always call equals with anything: other: Any?
Thank for answers. I think there is no way to prevent this at compilation level, so I decided to check type for other.
inline infix fun <reified T> T.isNullOr(other: T): Boolean {
if (this == null) return true
if (other !is T) return false
return this == other
}
If you really want to prevent it, you can:
class IsNullOr<T>(val x: T) {
operator fun invoke(other: T): Boolean {
if (x == null) return true
return x == other
}
}
fun <T> T.isNullOr() = IsNullOr(this)
fun main(args: Array<String>) {
val thisShouldWork = true.isNullOr()(true) // compiles
val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}
This makes type inference depend only on the receiver of isNullOr. If vals could be generic, you'd even keep the original syntax (but they can't).