Home > Mobile >  Kotlin Nullable with .toString() understanding Kotlin null safe
Kotlin Nullable with .toString() understanding Kotlin null safe

Time:08-19

So i'm playing around will nullables and null safe in Kotlin to try understand it better.

var stringNull : String? = null
println(stringNull.toString())
println(stringNull?.lowercase())

Both of these return "null" so I'm just trying to figure out what the difference between String and String? is in practical terms. Is the "nullness" of the String not stored in the String Class itself? is it the method, such as toString() or lowerCase(), that is handling the "nullness" passed to it by an operator and not the String class itself? it's hard to see what's happening here in terms of the literal data that is being passed around. how is the value of "null", as in no data, being parsed into a string value of "null"?

CodePudding user response:

The reason println(stringNull.toString()) prints null is because Kotlin decided to create an extension function on Any? called toString() over here.

If you try

class SomeClass {
    fun foo() {}
}

fun main() {
    val some: SomeClass? = null
    println(some?.foo())
    println(some.foo())
}

The compiler will complain with Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type SomeClass?

toString is just a very special function so they decided to add it on every type, hence the Any?

P.S Also, I would recommend to use val by default (even when playing around) and only use var if you really really need to.

CodePudding user response:

stringNull.toString() calls the following extension function:

/**
 * Returns a string representation of the object. Can be called with a null receiver, in which case
 * it returns the string "null".
 */
public fun Any?.toString(): String

stringNull?.lowercase() call the following extension function:

/**
 * Returns a copy of this string converted to lower case using Unicode mapping rules of the invariant locale.
 *
 * This function supports one-to-many and many-to-one character mapping,
 * thus the length of the returned string can be different from the length of the original string.
 *
 * @sample samples.text.Strings.lowercase
 */
@SinceKotlin("1.5")
@WasExperimental(ExperimentalStdlibApi::class)
public expect fun String.lowercase(): String

So while the two calls both return null, it's two different function calls.

CodePudding user response:

Is the "nullness" of the String not stored in the String Class itself?

If I understand your question correctly, that is correct, String is itself not nullable. String? is the nullable version of String.

is it the method, such as toString() or lowerCase(), that is handling the "nullness" passed to it by an operator and not the String class itself?

Among toString and lowerCase, only toString handles nulls. We can see this from their signatures:

public fun Any?.toString(): String
//         ^^^^
//    nullable type
public expect fun String.lowercase(): String
//                ^^^^^^
//           non-nullable type

In the internal implementation of toString, you can imagine that there is a bit of logic that says "if the instance is null, return "null"".

lowercase cannot be called on a nullable string directly, and will give you a compiler error if you try to do that. This is why you have to use the ?. safe navigation operator to call it on stringNull. This will check if stringNull is null before calling lowercase, and if it is found that stringNull is null, lowercase won't actually be called, and the whole expression stringNull?.lowercase() evaluates to null. Therefore, "null" is printed.

(And I think I just answered your last question)

  • Related