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
Related
The following is an example of this problem. Why am I unable to invoke supplier using the operator function? If I make supplier non-nullable Supplier<Int> I have no search problem
operator fun <T> Supplier<T>.invoke(): T = this.get()
val supplier: Supplier<Int>? = (Supplier<Int> { 5 })
if (supplier != null) {
// Fails: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Supplier<Int>?
supplier()
supplier.invoke() // fine, smart casts
supplier.get() // fine, smart casts
}
This problem was fixed for me in a later of version of IntelliJ + Kotlin plugin
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()
}
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") }
If I have a code fragment like
val myNullableString : String?
val resultingString = if (myNullableString.isNullOrBlank())
myNullDefaultString else String.format(myNullableString!!, someOtherString)
I have to use myNullableString!! in String.format() since the compiler would not be able to figure out that isNullOrBlank() includes a null-check. Is this correct or is there any way to tell the compiler that a function will infer that the instance is not null?
Kotlin 1.3 introduced a concept called contracts. From now on you can tell the compiler that a null check has been done. It has already been implemented for all necessary functions in the standard library (for isNullOrBlank() too).
So, your example would compile now without !!
val myNullableString: String? = null
val result = if (myNullableString.isNullOrBlank()) {
"default"
} else {
String.format(myNullableString, "hello") // myNullableString is smart-casted to String
}
Didn’t see any mentions that there is a way to tell such info to the compiler, but here is a workaround: having function returning null if string is empty and use let?.{} after that
inline fun String?.nullIfBlank(): String? = if (this?.isBlank() ?: true) null else this
val resultingString: String = myNullableString.nullIfBlank()?.let {
String.format(it)
} ?: myNullDefaultString
Compiler can perform smart cast from nullable type to non-nullable only after explicit check expression, like myNullableString != null
The code below will not compile because the variable myType can be null. Is there a way of executing a with block for nullable types in Kotlin?
val myType: MyType? = null
with(myType) {
aMethodThatBelongsToMyType()
anotherMemberMethod()
}
You can convert a nullable type to a non-nullable type with the suffix !!:
with(myType!!) {
aMethodThatBelongsToMyType()
anotherMemberMethod()
}
If the value is indeed null, it will throw a NullPointerException, so this should generally be avoided.
A better way to do this is to make the execution of the code block dependent on the value being non-null by making a null-safe call and using the apply extension function instead of with:
myType?.apply {
aMethodThatBelongsToMyType()
anotherMemberMethod()
}
Yet another option is to check if the value is non-null with an if statement. The compiler will insert a smart cast to a non-nullable type inside the if-block:
if (myType != null) {
with(myType) {
aMethodThatBelongsToMyType()
anotherMemberMethod()
}
}
You could define your own with function that accepts nullables, then determines whether to actually run based on whether the object is null.
Like this:
fun <T, R> with(receiver: T?, block: T.() -> R): R? {
return if(receiver == null) null else receiver.block()
}
Then you can call the code the way you wanted to in the example with no issues, and the result will equal null if what you pass in is null.
Or, if the code block should (and could) be run either way, even if myType is null, then you'd define it like this instead:
fun <T, R> with(receiver: T?, block: T?.() -> R): R {
return receiver.block()
}