In lombok extension method obj.method() is a syntax sugar for SomeUtil.method(obj). It allows for obj be null.
Kotlin extensions methods are resolved statically so I assume it's the same syntactic sugar. But when I wrote
fun Any.stringOrNull() = this?.toString()
I got a warning about unnecessary safe call on non-null receiver. Does that mean I can't call extension functions on null objects like with Lombok?
You can call it on a nullable object if you define it to be an extension on a nullable type:
fun Any?.stringOrNull() = ...
Otherwise, like with any other method, you'd have to use the safe call operator.
You can create extensions on nullable receiver types. In your example, it has to be Any? instead of Any which would not allow null, see the docs:
Nullable Receiver
Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null, and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
Be careful, for:
fun Any?.toString(): String
following behavior:
var obj: Any? = null
obj?.toString() // is actually null
obj.toString() // returns "null" string
just spent 15 very frustrating minutes before realized this...
val string: String? = "Hello World!"
print(string.length)
// Compile error: Can't directly access property of nullable type.
print(string?.length)
// Will print the string's length, or "null" if the string is null.
?. Safe Call operator for nullable receiver##
The safe call operator returns null if the value to the left is null, otherwise continues to evaluate the expression to the right, so in order to call any function on nullable receiver you need to use safe call operator after Any.(Use Any?)
Then you can check for null value of this(here this object points to receiver) inside function body.This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
Related
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"?
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.
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)
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.
I'm new to Kotlin and these two below codes give different results.
fun main() {
var name: String? = "Rajat"
name = null
print(name?.toLowerCase())
}
Output: Compilation Error (illegal access operation)
fun main() {
var name: String? = null
print(name?.toLowerCase())
}
Output: null
When you do this assignment:
name = null
name is smart casted to Nothing?, which is problematic. Nothing is the subtype of every type, and so you become able to call any accessible extension functions of any type, according to the overload resolution rules here.
Compare:
fun main() {
var name: String? = "Denis"
name = null
print(name?.myExtension()) // works
val nothing: Nothing? = null
print(nothing?.myExtension()) // also works
}
fun Int.myExtension(): Nothing = TODO()
Note that allowing you to call any extension function on Nothing is perfectly safe - name is null anyway, so nothing is actually called.
Char.toLowerCase and String.toLowerCase happen to be two of the extension functions that are accessible, and you can call both on name, which is now a Nothing?. Therefore, the call is ambiguous.
Note that smart casts only happens in assignments, not in initialisers like var name: String? = null. Therefore, name is not smart casted to Nothing? in this case:
fun main() {
var name: String? = null
print(name?.toLowerCase()) // better to use lowercase(), toLowerCase is deprecated!
}
For the reason why, see my answer here.
The actual error on your first example is
Overload resolution ambiguity: public inline fun Char.toLowerCase(): Char defined in kotlin.text public inline fun String.toLowerCase(): String defined in kotlin.text
Looks like the Kotlin compiler is being too smart for its own good here. What's happening, is that on the second example, you are explicitly defining a variable of type String? and assigning it some value (null in this case, but that doesn't matter).
On the second example, you are defining a variable of some type, and then telling the compiler "hey, after this assignment, name is always null". So then it remembers the more-specific "name is null" instead of "name is String?".
The standard library has two methods called toLowerCase, one on Char and one on String. Both of them are valid matches now, and the compiler is telling you it doesn't know which one to pick. In the end that won't matter, because name is null, but the compiler apparently doesn't use that final thing to throw out the method call altogether.
I am trying check nullable object with extension function, but smart casting not work after calling this function.
fun <T> T?.test(): T = this ?: throw Exception()
val x: String? = "x"
x.test()
x.length // Only safe (?.) or non-null asserted (!!) calls are allowed on a nullable receiver of type String?
Is it a Kotlin bug? If not, why there is no implicit casting?
As #Madhu Bhat mentioned in comment above, your variable 'x' is still nullable.
You may use your function simply like this:
x.test().length
Otherwise you can check for null by following inline function and then perform any functions directly on the variable. (Note the usage of 'contract' and annotations '#ExperimentalContracts')
#ExperimentalContracts
fun <T> T?.notNull(): Boolean {
contract {
returns(true) implies (this#notNull != null)
}
return this != null
}
Now you can use this function like this
if(x.notNull()){
x.length
}
But its not seems so useful if your using this function just to check nullability.
Check here to know more about Kotlin contracts
This question already has an answer here:
Why doesn't toString throw an exception when called on null value in Kotlin? [duplicate]
(1 answer)
Closed 3 years ago.
In my android project I have overriden onCheckedChanged() like so:
var numberOfPlayers: Int = 0
override fun onCheckedChanged(group: RadioGroup?, checked: Int) {
val chosen = activity?.findViewById<RadioButton>(checked)?.text
numberOfPlayers = chosen.toString().toInt()
}
And I'm confused why numberOfPlayers isn't underlined red as chosen may be null - therefore I'm calling toString() on a possible null value. Why won't this cause a NullPointerException?
.toString() has a safety, meaning if it receives a null value it will return "null" string.
As stated in the official documentation:
fun Any?.toString(): String
Returns a string representation of the object. Can be called with a
null receiver, in which case it returns the string "null"
Normal toString() from kotlin.Any should throw exception if value is null. But, there is also method Any?.toString() from kotlin.kotlin_builtins.
As kotlin.Any.toString cannot be applied to nullable type your compiler knows what method should use.
See this example:
fun test() {
val possibleNull: Any? = Any()
val notNull: Any = Any()
possibleNull.toString()
possibleNull?.toString()
possibleNull!!.toString()
notNull.toString()
}
If you write this in IntelliJ you'll see that the first toString() is actually extenstion method, because that one can be applied to that type. All others examples will call "normal" toString() which would work as you told.
I have the following code fragment:
val foo: String? = null
foo.run { println("foo") }
I have here a nullable variable foo that is actually set to null followed by a nonsafe .run() call.
When I run the code snippet, I get foo printed out despite the fact that the run method is called on a null. Why is that? Why no NullPointerException? Why does compiler allow a nonsafe call on an optional value?
If I pass println(foo), I get a nice juicy null in the console so I think it's safe to assume that foo is actually null.
I believe, there are two things that both might be of some surprise: the language semantics that allow such a call, and what happens at runtime when this code executes.
From the language side, Kotlin allows nullable receiver, but only for extensions. To write an extension function that accepts a nullable receiver, one should either write the nullable type explicitly, or use a nullable upper bound for a type parameter (actually, when you specify no upper bound, the default one is nullable Any?):
fun List<*>?.isEmptyOrNull() = this == null || size == 0 // explicit nullable type
fun <T : CharSequence?> T.nullWhenEmpty() = if ("$this" == "") null else this // nullable T
fun <T> T.identity() = this // default upper bound Any? is nullable
This feature is used in kotlin-stdlib in several places: see CharSequence?.isNullOrEmpty(), CharSequence?.isNullOrBlank(), ?.orEmpty() for containers and String?.orEmpty(), and even Any?.toString(). Some functions like T.let, T.run that you asked about and some others just don't provide an upper bound for the type parameter, and that defaults to nullable Any?. And T.use provides a nullable upper bound Closeable?.
Under the hood, that is, from the runtime perspective, the extension calls are not compiled into the JVM member call instructions INVOKEVIRTUAL, INVOKEINTERFACE or INVOKESPECIAL (the JVM checks the first argument of such calls, the implicit this, for being null and throws an NPE if it is, and this is how Java & Kotlin member functions are called). Instead, the Kotlin extension functions are compiled down to static methods, and the receiver is just passed as the first argument. Such a method is called with the INVOKESTATIC instruction that does not check the arguments for being null.
Note that when a receiver of an extension can be nullable, Kotlin does not allow you to use it where a not-null value is required without checking it for null first:
fun Int?.foo() = this + 1 // error, + is not defined for nullable Int?
To add to what #holi-java said, there is nothing unsafe about your code at all. println("foo") is perfectly valid whether foo is null or not. If you tried something like
foo.run { subString(1) }
it would be unsafe, and you will find it won't even compile without some sort of null check:
foo.run { this?.subString(1) }
// or
foo?.run { subString(1) }
This is because the top-level function run accept anything Any & Any?. so an extension function with Null Receiver doesn't checked by Kotlin in runtime.
// v--- accept anything
public inline fun <T, R> T.run(block: T.() -> R): R = block()
Indeed, the inline function run is generated by Kotlin without any assertions if the receiver can be nullable, so it is more like a noinline function generated to Java code as below:
public static Object run(Object receiver, Function1<Object, Object> block){
//v--- the parameters checking is taken away if the reciever can be nullable
//Intrinsics.checkParameterIsNotNull(receiver, "receiver");
Intrinsics.checkParameterIsNotNull(block, "block");
// ^--- checking the `block` parameter since it can't be null
}
IF you want to call it in a safety way, you can use safe-call operator ?. instead, for example:
val foo: String? = null
// v--- short-circuited if the foo is null
foo?.run { println("foo") }