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()
orlowerCase()
, that is handling the "nullness" passed to it by an operator and not theString
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)