Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass nullable type into function that takes a non null type?

Tags:

kotlin

Is this possible if I do a null check before passing? For example:

fun main(args: Array<String>) {
    var num: Int? = null
    // Stuff happens that might make num not null
    ...
    if (num != null) doSomething(num)
}

fun doSomething(number: Int) {
    ...
}

I don't understand why the compiler won't allow me to pass a nullable even though I check that it's not null first. Can anyone explain?

like image 743
user3307102 Avatar asked Apr 23 '15 18:04

user3307102


People also ask

Can nullable be null?

Conversion from a nullable value type to an underlying type At run time, if the value of a nullable value type is null , the explicit cast throws an InvalidOperationException.

Can we pass null to a function?

You can pass NULL as a function parameter only if the specific parameter is a pointer. The only practical way is with a pointer for a parameter. However, you can also use a void type for parameters, and then check for null, if not check and cast into ordinary or required type.

Can nullable be null C#?

Nullable is a term in C# that allows an extra value null to be owned by a form. We will learn in this article how to work with Nullable types in C#. In C#, We have majorly two types of data types Value and Reference type. We can not assign a null value directly to the Value data type.

How do you pass null in Kotlin?

Kotlin neither allows null values to be passed as parameter values nor allows null object references unless you specify that a variable can be null . In other words, Kotlin makes you tell the compiler "this (or that) variable can be null ." Such variables are referred to as nullable values.


4 Answers

NOTE: starting from compiler version 1.0 beta the code in question works as is

The compiler can tell if the variable is mutated between check and use, at least in case of local variables like in this question, and in some other cases. See Jayson's answer for details.


http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-keyword--in-conditions says

The compiler tracks the information about the [null] check ... this only works where b is immutable (i.e. a local val or a member val which has a backing field and is not overridable), because otherwise it might happen that b changes to null after the check.

So something like this should work:

fun main(args: Array<String>) {
    var num: Int? = null
    // Stuff happens that might make num not null
    ...
    val numVal: Int? = num
    if (numVal != null) doSomething(numVal)
}

fun doSomething(number: Int) {
    ...
}

Of course, it would be nicer to rewrite "stuff happens" in such a way that you could make num into a val in the first place.

like image 195
npostavs Avatar answered Nov 03 '22 01:11

npostavs


In current Kotlin (1.0 beta or newer) you do not have this issue anymore. Your code would compile. A local variable that is val or var can safely Smart Cast since the compiler can determine if the value could have mutated or not (on another thread for example).

Here is an excerpt from another Stack Overflow question that covers more aspects of nullability and Kotlin's operators for dealing with them.

More about null Checking and Smart Casts

If you protect access to a nullable type with a null check, the compiler will smart cast the value within the body of the statement to be non nullable. There are some complicated flows where this cannot happen, but for common cases works fine.

val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
   // allowed to reference members:
   possiblyXyz.foo()
   // or also assign as non-nullable type:
   val surelyXyz: Xyz = possibleXyz
}

Or if you do a is check for a non nullable type:

if (possibleXyz is Xyz) {
   // allowed to reference members:
   possiblyXyz.foo()
}

And the same for 'when' expressions that also safe cast:

when (possibleXyz) {
    null -> doSomething()
    else -> possibleXyz.foo()
}

// or

when (possibleXyz) {
    is Xyz -> possibleXyz.foo()
    is Alpha -> possibleXyz.dominate()
    is Fish -> possibleXyz.swim() 
}

Some things do not allow the null check to smart cast for the later use of the variable. The example above uses a local variable that in no way could have mutated in the flow of the application, whether val or var this variable had no opportunity to mutate into a null. But, in other cases where the compiler cannot guarantee the flow analysis, this would be an error:

var nullableInt: Int? = ...

public fun foo() {
    if (nullableInt != null) {
        // Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
        val nonNullableInt: Int = nullableInt
    }
}

The lifecycle of the variable nullableInt is not completely visible and may be assigned from other threads, the null check cannot be smart cast into a non nullable value. See the "Safe Calls" topic below for a workaround.

Another case that cannot be trusted by a smart cast to not mutate is a val property on an object that has a custom getter. In this case the compiler has no visibility into what mutates the value and therefore you will get an error message:

class MyThing {
    val possibleXyz: Xyz? 
        get() { ... }
}

// now when referencing this class...

val thing = MyThing()
if (thing.possibleXyz != null) {
   // error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
   thing.possiblyXyz.foo()
}

read more: Checking for null in conditions

like image 43
2 revs, 2 users 99% Avatar answered Nov 03 '22 00:11

2 revs, 2 users 99%


You can use let to simplify the code. The kotlin scope function introduces a local variable in the context of "num". No need to declare temporary variable numVal.

fun main(args: Array<String>) {
    var num: Int? = null
    // Stuff happens that might make num not null
    ...
    num?.let{
        doSomething(it)
    }
}

Which works same as below but simpler and cleaner.

fun main(args: Array<String>) {
    var num: Int? = null
    // Stuff happens that might make num not null
    ...
    val numVal: Int? = num
    if (numVal != null) doSomething(numVal)
}
like image 25
Ray Lee Avatar answered Nov 03 '22 00:11

Ray Lee


Use can use Scoping function let or apply along with null safe operator ?.

 fragmentManager?.let{
        viewPager.adapter = TasksPagerAdapter(it)
    }

This way you can pass a nullable type to a non-nullable type parameter

like image 34
Alok Gupta Avatar answered Nov 03 '22 02:11

Alok Gupta