Warning: "Safe call on a non-null receiver will have nullable type in future releases" due to nullable variable in buildvariants - kotlin

I'm developing an android app using Kotlin.
I have two different build-variants - VariantA and VariantB - both containing a configuration-File:
// VariantA
object ConfigEnvironment {
val SOME_PARAMETER: String? = null
}
// VariantB
object ConfigEnvironment {
val SOME_PARAMETER: String = "This is a config"
}
In my code i'm calling:
ConfigEnvironment.SOME_PARAMETER?.let { Log.d("tag", "$it" }
When building VariantB the Compiler throws a warning:
Safe call on a non-null receiver will have nullable type in future releases
While this is correct for this variant, it's somewhat impractical - since i need the nullability in the other variant.
Can i safely supress this lint?
(And how do i do that? My IDE didn't suggest any fixes)

It should be safe to suppress this warning since you do not call something which expects a non-nullable expression for it inside the let.
You can suppress the warnung like this:
#Suppress("UNNECESSARY_SAFE_CALL")
ConfigEnvironment.SOME_PARAMETER?.let { Log.d("tag", "$it") }
IntelliJ can help you with that. Just move the cursor to the ?. and type your shortcut for Quick Fix (you can look it up in the Keyboard settings):

Related

Kotlin error Smart cast to 'X' is impossible, because 'state' is a property that has open or custom getter when trying to observe state

I'm try to observe state as you see but when i use when and try to get data, compiler says Smart cast is impossible by casting it solves the problem but It felt like i'm doing it in wrong way, i want to know there is any other solution to fix this error.
sealed class Response<out T : Any> {
object Loading : Response<Nothing>()
data class Success<out T : Any>(val data: T) : Response<T>()
data class Error(val error: ResultError, val message: String? = null) : Response<Nothing>()
}
val userState by userViewModel.userState.collectAsState()
when(userState){
is Response.Error -> userState.error // Smart cast to 'Response.Error' is impossible, because 'userState' is a property that has open or custom getter
Response.Loading -> Unit
is Response.Success -> userState.data // Smart cast to 'Response.Success<User>' is impossible, because 'userState' is a property that has open or custom getter
}
This line:
val userState by userViewModel.userState.collectAsState()
Defines userState through a delegate, so the compiler cannot guarantee that subsequent reads of the property's value will give the same value. In particular here, it means the access in the when() condition and the access within the when's branches might not return the same value from the compiler's point of view, thus it cannot smart cast.
You could use an intermediate variable here:
val userState by userViewModel.userState.collectAsState()
when(val s = userState){
is Response.Error -> s.error
Response.Loading -> Unit
is Response.Success -> s.data
}
Now since s is a local val the compiler can guarantee it will have the same value in the condition and in the when branches, and smart casting works
Compiler can only perform smart casts when it can guarantee that the value won't change with time. Otherwise, we might get into the situation where after the type check the variable changed to another value and does no longer satisfy the previous constraint.
Delegated properties (ones declared with by keyword) are much different than "normal" variables. They don't really hold any value, but each time we access them, we actually invoke getValue() (or setValue()) on their delegate. With each access the delegate may provide a different value. Compiler can't guarantee immutability of the value and therefore smart casts are disallowed.
To fix this problem, we need to create a local copy of the data that is delegated. This is like invoking getValue() and storing the result as a local variable, so it can no longer change. Then we can perform smart casts on this local data copy. It can be understood better with the following example:
fun main() {
val delegated by Delegate()
println(delegated) // 0
println(delegated) // 1
println(delegated) // 2
val local = delegated // `local` set to 3
println(local) // 3
println(delegated) // 4
println(local) // 3
}
class Delegate {
var i = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return i++
}
}
Each time we access delegated it returns a different value. It may change between null and not null or even change the type entirely. When we assign it to local we take "current" value of delegated and store its copy locally. Then delegated still changes with each access, but local is constant, so we can perform smart casts on it.
Depending on your case, if there is a way to acquire "current" or "direct" value of userViewModel.userState.collectAsState() then you can use it when assigning to userState - then it should work as you expect. If there is no such function, then I think the easiest is to use another variable to store a local copy, like this:
val _userState by userViewModel.userState.collectAsState() // delegated
val userState = _userState // local copy, immutable
when(userState){
is Response.Error -> userState.error // Smart cast to 'Response.Error' is impossible, because 'userState' is a property that has open or custom getter
Response.Loading -> Unit
is Response.Success -> userState.data // Smart cast to 'Response.Success<User>' is impossible, because 'userState' is a property that has open or custom getter
}

Allow !! when it is certain it will not throw an NPE

I'm looking at a pull request, which contains !! - the not-null assertion operator. Should it be approved?
The nullable value itself will indeed never be null. I can't think of a better argument than 'It feels wrong'
private val hasFinishedSubject = BehaviorSubject.create<Boolean>()
init {
hasFinishedSubject.onNext(false)
}
val hasFinishedScanner: Boolean
get() = hasFinishedSubject.value!!
Since hasFinishedSubject is initialized on construction, hasFinishedSubject.value will never be null
It could be replaced with f.i.:
val hasFinishedScanner: Boolean
get() = hasFinishedSubject.value ?: false
but this false is redundant and could lead to bugs as well. What is the most clean?
Kotlin compiler respect nullability annotations from Java, e.g. #Nullable or #NotNull from either JSR-305 or intelliJ.
https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
It may probably make sense to include these annotations into the BehaviorSubject class if you have control over it.
From the other side, there are Platform types in Kotlin, that are used to simplify nullability check in Java interop.
https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types
From my point of view, using !! with Kotlin code should be avoided, in Kotlin there are enough ways to make your code Nullable-correct.
For the Java interop - it may happen. But instead of using !! one may use something more meaningful, like what you suggested above.
I would probably have written something like:
val hasFinishedScanner: Boolean
get() = hasFinishedSubject.value ?: error("No value")
Here the error function has return type Nothing, in fact, it throws an exception.

Why do unsafe .run() call works fine on a null value in Kotlin?

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") }

Kotlin idiom for working with non-null object and non-blank String representation

I have a nullable property (a Java object) that knows how to convert itself to a String, and if this representation is not empty, I would like to do something with it. In Java this looks like:
MyObject obj = ...
if (obj != null) {
String representation = obj.toString();
if (!StringUtils.isBlank(representation)) {
doSomethingWith(representation);
}
}
I'm trying to find the most idiomatic way of converting this to Kotlin, and I have:
with(obj?.toString()) {
if (!isNullOrBlank()) {
doSomethingWith(representation)
}
}
But it still feels like too much work for such a simple operation. I have this feeling that combining let, when, and with I can slim this down to something a bit shorter.
The steps are:
If the object (A) is not null
If the String representation (B) of object (A) is not blank
Do something with (B)
I tried:
when(where?.toString()) {
isNullOrBlank() -> builder.append(this)
}
But (1) it fails with:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: #InlineOnly public inline fun
CharSequence?.isNullOrBlank(): Boolean defined in kotlin.text #InlineOnly public inline fun CharSequence?.isNullOrBlank(): Boolean defined in
kotlin.text
And even if it got past that, (2) it would want the exhaustive else, which I don't really care to include.
What's the "Kotlin way" here?
You can use the (since Kotlin 1.1) built-in stdlib takeIf() or takeUnless extensions, either works:
obj?.toString().takeUnless { it.isNullOrBlank() }?.let { doSomethingWith(it) }
// or
obj?.toString()?.takeIf { it.isNotBlank() }?.let { doSomethingWith(it) }
// or use a function reference
obj?.toString().takeUnless { it.isNullOrBlank() }?.let(::doSomethingWith)
For executing the action doSomethingWith() on the final value, you can use apply() to work within the context of the current object and the return is the same object, or let() to change the result of the expression, or run() to work within the context of the current object and also change the result of the expression, or also() to execute code while returning the original object.
You can also create your own extension function if you want the naming to be more meaningful, for example nullIfBlank() might be a good name:
obj?.toString().nullIfBlank()?.also { doSomethingWith(it) }
Which is defined as an extension to a nullable String:
fun String?.nullIfBlank(): String? = if (isNullOrBlank()) null else this
If we add one more extension:
fun <R> String.whenNotNullOrBlank(block: (String)->R): R? = this.nullIfBlank()?.let(block)
This allows the code to be simplified to:
obj?.toString()?.whenNotNullOrBlank { doSomethingWith(it) }
// or with a function reference
obj?.toString()?.whenNotNullOrBlank(::doSomethingWith)
You can always write extensions like this to improve readability of your code.
Note: Sometimes I used the ?. null safe accessor and other times not. This is because the predicat/lambdas of some of the functions work with nullable values, and others do not. You can design these either way you want. It's up to you!
For more information on this topic, see: Idiomatic way to deal with nullables

swift closure stored and access as a variable

I want to implement a callback in a swift project just like I used to do in Objective-C
I need a variable of type closure. That closure should take as a parameter an object and return nothing.
var downloadCompleted: (MLBook) -> (Void)!
When I need to trigger the callback I do this:
if self.downloadCompleted {
self.downloadCompleted(book)
}
The compiler complains with this error message:
Type '(MLBook) -> (Void)!' does not conform to protocol 'BooleanType'
If I remove the if statement the compiler says:
Property 'self.downloadCompleted' not initialized
even though it's implicitly unwrapped.
When I try to get the callback:
BookStore.sharedInstance.downloadCompleted{(book: MLBook) -> () in
println("Print if you got the callback")
}
I get this error message:
'(MLBook) -> ()' is not convertible to 'MLBook'
I'm more worried about the last error message as I'm not quite sure what it is trying to tell me.
Any help would be appreciated. Thanks
Here is your working example. You have a number of mistakes which the attached illustrates. Note
I had the download() method return Bool so that the result can be see in this screen shot.
But, your use of an implicitly unwrapped optional (aka '!') is incorrect. Such an optional is used when the value may be nil but will be assigned at a known time and not changed (see Apple documentation for a description). Your downloadCompleted is a true optional (at least in your example use). Thus, better code, which turns out to be slightly simpler is:
2 mistakes. 1st, The whole type should be wrapped in (), then followed a ? or ! as a optional or implicit unwrapped optional. 2nd, you should check with nil, in swift, no implicit boolean conversion.
In your use case, you should use Optional instead of Implicit unwrapped. Because there is big chance that your property has a nil value. With IUO(Implicit unwrapped optional), you skip compiler warning and will get a runtime error.
import Foundation
class MLBook {
var name = "name"
}
class A {
var downloadCompleted: ((MLBook) -> Void)?
func down(){
var book = MLBook()
if let cb = self.downloadCompleted {
cb(book)
}
}
}
var a = A()
a.downloadCompleted = {
(book: MLBook) -> Void in
println(book.name)
}
a.down()