How can I let the Kotlin compiler know I already checked for null inside a function? - kotlin

Basically I have a function that does some stuff but specifically it checks if two values I'm passing are null. This function is called from various places to make sure those two values are not null. Kotlin is complaining later that I'm not checking for null. Is there a way I can do this so that Kotlin already knows that I am checking for null without using !!?
Here is a simple example:
private fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
private fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
private fun testNull(possibleNull: String?): Boolean {
return possibleNull != null
}
Basically testNull is only true if possibleNull is not null and that check is on an if right before calling mustPassNonNull so can I let Kotlin know I'm already checking that? without using !! of course.
Thanks.

It is possible with the use of contracts. Currently in experimental in Kotlin 1.3.
It is possible to declare contracts for your own functions, but this feature is experimental, as the current syntax is in a state of early prototype and will most probably be changed. Also, please note, that currently the Kotlin compiler does not verify contracts, so it's a programmer's responsibility to write correct and sound contracts. -kotlinlang.org
#ExperimentalContracts
fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
#ExperimentalContracts
fun testNull(possibleNull: String?): Boolean {
contract{
returns(true) implies (possibleNull is String)
}
return possibleNull != null
}
Articles I referenced:
https://kotlinlang.org/docs/reference/whatsnew13.html
https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2

It seems like a simple let situation
private fun stuff() {
var possibleNull: String? = "test"
possibleNull?.let { mustPassNonNull(it) }
}
This way mustPassNonNull will know that it isn't null :)
Also, if you need to do more than just check for nullability you could do:
possibleNull
?.takeIf { /* it's not null here anymore, add any checks you need */}
?.let { /* both non-null and checked for whatever you need */}

Related

Kotlin Contracts not working for null-check in extension function

I'm trying to write an extension function that returns true if the value is not null or 0 and use a contract to guarantee to the compiler that if I return true, the value is non-null. However, it doesn't seem to be working with smart casting. It still won't compile when I try to pass the value into a function that takes a non-nullable Long.
I tried to compile this code and it would not work. I expected the id to be smart-casted to a Long from a Long? since the contract guarantees that if isValidId returns true then the passed in Long? is not null.
As you can see, the property is immutable, so I don't think that's the issue. I also added some more code below, because the problem appears to be specific to extension functions. It works when I pass ID as a traditional parameter.
fun test() {
val id: Long? = null //5L
if (id.isValidID()) {
// It won't compile because the compiler says that id is a Long?
// instead of smart casting it to a Long. doWithId requires a Long.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun Long?.isValidID(): Boolean {
contract { returns(true) implies (this#isValidID != null) }
return this != null && this != 0L
}
Thanks!
EDIT:
Oh! It works when it's not an extension function. Does anybody know how I can make extension functions work?
fun test() {
val id: Long? = null
if (isValidID(id)) {
// This compiles now that I pass ID as a param instead of
// passing it in an extension function.
doWithId(id)
}
}
fun doWithId(id: Long) {}
#OptIn(ExperimentalContracts::class)
fun isValidID(id: Long?): Boolean {
contract { returns(true) implies (id != null) }
return id != null && id != 0L
}

Is it possible to null T or 'this' at the end of generic function for Kotlin?

I have in my project a listener. It is assigned to drawerLayout. I would like to in lambda function remove it and null it at once (sequentially). Is it possible to null T or this at the end of generic function.
Here is my code:
// Usage
actionBarListener?.let {
drawerLayout.removeDrawerListener(it) // remove listener
actionBarListener = null // null it
}
// Usage expected
actionBarListener.releaseAndSetNull {
drawerLayout.removeDrawerListener(it) // remove listener and null it
}
// Generic
fun <T> T?.releaseAndSetNull(block: (T?) -> Unit) = apply {
this?.apply { block.invoke(this) }
this = null // Error: variable expected
}
As Ivo Beckers said, this function would only work on vars, i.e. KMutableProperty0<T>. So you could write an extension on KMutableProperty0<T?>, and use reflection to set it, if you don't mind using reflection, that is.
inline fun <T: Any> KMutableProperty0<T?>.releaseAndSetNull(block: (T?) -> Unit) {
block(this.get())
this.set(null)
}
// or if you don't want the block to be called if the property is null:
inline fun <T: Any> KMutableProperty0<T?>.releaseAndSetNull(block: (T) -> Unit) {
this.get()?.run(block)
this.set(null)
}
Then suppose you have a property:
var foo: Int? = 10
You can do:
::foo.releaseAndSetNull { println("Foo: $it") }
// or if foo belongs to someObject
someObject::foo.releaseAndSetNull { println("Foo: $it") }
Looking at the generated bytecode, the way this is implemented (which is subject to change) is that each unique property referred to by a property reference in this way causes an inner class to be generated. The inner class will then have get and set methods that do their jobs with little extra cost - as they can just set the right property directly. So really the main cost is the extra inner class that is generated.
I can think of several reasons why this could never work.
First of, the generic function doesn't know if this is a var or val. And this functionality could only works on a var
Likewise, it can't know if it's nullable, that's also a requirment.
Furthermore, it can even be the case that it's not a variable that's calling the function.
Like say you have
fun getActionBarListener() {
return actionBarListener
}
Then somewhere else you could do
getActionBarListener().releaseAndSetNull {
drawerLayout.removeDrawerListener(it) // remove listener and null it
}
How do you expect that to work?
Or even anonymous objects could call this function.

Conditional nullable return type in kotlin

I wrote some code and that work!
public fun toTimeStamp(epoch: Long?): String? = when (epoch) {
null -> null
else -> toTimeStamp(epoch)
}
public fun toTimeStamp(epoch: Long): String =
TIMESTAMP_PATTERN.print(toGregorianDateTime(epoch))
but when i converted it to extention function dosent work.
compiler say method name is duplicated.
I need somthing like this :
fun Long?.toDate() : String? {
// some code
}
fun Long.toDate() : String {
// some code
}
or is there annotation to say if input parameter is null return type is null too ?
By the looks of it, your objective can be accomplished with safe calls.
Say you had this function:
fun Long.toDate(): String =
TIMESTAMP_PATTERN.print(toGregorianDateTime(epoch))
You can use it on a nullable long like so:
myNullableLong?.toDate()
That will return null if the long is null, and the correct date otherwise.
The problem is that you'll have to think about how this looks in the JVM when using kotlin for the JVM. Your methods:
fun Long?.toDate() : String? {
// some code
}
fun Long.toDate() : String {
// some code
}
Are equivalent in Java to:
public static #Nullable String toDate(#Nullable Long receiver) {}
public static #NonNull String toDate(long receiver) {}
Unfortunately, in Java annotations do nothing to resolve ambiguity of declarations (neither return types), so essentially these methods are the same and that's why the compiler complains.
Like some already mentioned, you most likely can just use safe calls.
Declare an extension on Long and whenever this long can be null, just call ?.toDate on it. Just like #llama Boy suggested.
This will achieve what you want:
When input is nullable, output will be nullable too
When input is not nullable, output will be not nullable too.
You can avoid the problem by using #JvmName annotation on one of them:
#JvmName("toDateNullable")
fun Long?.toDate() : String? {
// some code
}
fun Long.toDate() : String {
// some code
}
but I agree with the other answers that in most cases you can prefer just using safe calls instead of defining a separate Long?.toDate.

Retain smartcast when creating custom extension

I currently have to write
val myList: List<Int>? = listOf()
if(!myList.isNullOrEmpty()){
// myList manipulations
}
Which smartcasts myList to no non null. The below do not give any smartcast:
if(!myList.orEmpty().isNotEmpty()){
// Compiler thinks myList can be null here
// But this is not what I want either, I want the extension fun below
}
if(myList.isNotEmptyExtension()){
// Compiler thinks myList can be null here
}
private fun <T> Collection<T>?.isNotEmptyExtension() : Boolean {
return !this.isNullOrEmpty()
}
Is there a way to get smartCasts for custom extensions?
This is solved by contracts introduced in Kotlin 1.3.
Contracts are a way to inform the compiler certain properties of your function, so that it can perform some static analysis, in this case enable smart casts.
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
#ExperimentalContracts
private fun <T> Collection<T>?.isNotEmptyExtension() : Boolean {
contract {
returns(true) implies (this#isNotEmptyExtension != null)
}
return !this.isNullOrEmpty()
}
You can refer to the source of isNullOrEmpty and see a similar contract.
contract {
returns(false) implies (this#isNullOrEmpty != null)
}

What is the best way to handle null situations in Kotlin when extending a Java class?

I'm really stuck with situation when something can be null in Kotlin. When a value cannot be null - it is clear. But how to handle situation when something can be null by default. Take a look at this example:
abstract class MapActivity: AppCompatActivity(), OnMapReadyCallback {
lateinit var map: GoogleMap
fun initializeMap() {
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
override fun onMapReady(map: GoogleMap?) {
this.map = map //error here
}
}
So I should handle that situation this way:
override fun onMapReady(map: GoogleMap?) {
if (map != null)
this.map = map
}
But what if map is null. What should I do in this case? I've faced this problem while using java.
Ok, in java, if map is null, we will get NPE.
And for me NPE is better than nothing. The program crashes and this is a signal for me, that something is wrong and I should fix it. In this way, I can't even trace my problem and why it is happen.
Yeah, we can write something like:
override fun onMapReady(map: GoogleMap?) {
if (map != null) {
this.map = map
} else {
Log.d("Activity","MapIsNull")
}
}
and there is some meaning in this code, yes.
But there is still some hesitation in me head about the whole null safety thing.
Safe calls for example is weird thing which produces null and (In my opinion) heisenbugs or bugs that are hard to catch.
Can someone explain to me what is going on here, and what are the best practices to use null-safety.
I'm pretty sure that this thing is useful, because I'm not as smart as guys in JB
The documentation of OnMapReadyCallback says explicitly that it "provides a non-null instance of GoogleMap". Consequently, the parameter type of your onMapReady method should use a non-null type.
Note that, when you implement a Java interface in Kotlin, you have a choice of whether to declare the parameters of the implementing method as nullable or non-null. The IDE marks them as nullable because it's the safe default, but you can change them to non-null if that's what you need.
About dealing with nulls: let's imagine that you need to have a method fun onMapReady(map: GoogleMap?) which would set a class property map.
You have two design options to choose between:
1) Your app will not work if map is null
class A {
lateinit var map: GoogleMap // never ever `null`
fun onMapReady(map: GoogleMap?) {
map = map!! // throws
}
fun onMapReady2(map: GoogleMap?) {
map = map ?: throw Exception("in case you care for the exception")
}
}
2) Null has special meaning for you and is ok to have:
class A {
var map: GoogleMap? = null // map is not ready yet
fun onMapReady(map: GoogleMap?) {
map = map
}
}
Of course, if your case is case (1) and you can control the API, then you should never have a nullable receiver map: GoogleMap? because it confuses the user of the class.
I see two separate questions:
1. Null handling
2. Kotlin specific syntax for null handling
First is not a kotlin specific problem. It depends, and #voddan pretty much explained available strategies.
Regarding syntax.
If you interested only in non-null values, use let()
override fun onMapReady(map: GoogleMap?) {
map?.let { m ->
// do your staff
// 'm' now is definitely not null
}
}
If you want to know when it is null, use usual if-else block
override fun onMapReady(map: GoogleMap?) {
if (map != null) {
this.map = map
} else {
Log.d("Activity","MapIsNull")
}
Sidenotes:
1] #Cedric have nice post about kotlin's let() and friends
2] check out #yole's comment. when overriding fun in IDEA arguments can be marked as '?', but in fact they aren't.