2

I would like to assign new value to my variable but if it is null I would like to break the flow, print out some logs and then call to return.

My variable's type is Image and it cannot be null.

Below is my solution.

Basic code I started:

val img = reader.acquireLatestImage() ?: return

Code I finally created:

val img : Image = (reader.acquireLatestImage() ?: (Log.d(TAG, "Image is null!").also { return })) as Image
  1. Is it the best way to do it?
  2. Is it possible to create extension for that?
1
  • If you need the cast, something fishy must be going on here. But it looks correct at first glance, so I wonder if you actually need it. Commented Aug 13, 2021 at 22:13

2 Answers 2

2

It turns out the compiler is not smart enough to deal with also here but if you used let it would work without the cast:

val img : Image = reader.acquireLatestImage() ?: Log.d(TAG, "Image is null!").let { return }

This is because the type of Log.d(...) is Unit, so the type of Log.d(...).also { ... } is also Unit in general. The compiler doesn't see that the non-local return in this specific case will prevent the whole expression Log.d(...).also { ... } from resulting in a value (in practice it should have the type Nothing, but the compiler doesn't see it and still gives it the type Unit).

let, on the contrary, returns the value of the lambda expression, which in this case is really of type Nothing and therefore is correctly understood by the compiler.

Another option would be to reverse your null-handling section:

val img = reader.acquireLatestImage() ?: return Unit.also { Log.d(TAG, "Image is null!!") }

Is it the best way to do it?

In this form, I'm not convinced even by my own versions, which are probably not the most maintainable way of doing it. I would personally favor a plain if statement here for the log and return. The smart casting of Kotlin with flow typing will allow you to use your img variable with type Image after the check, even though it starts out with type Image?:

val img = reader.acquireLatestImage() // this is of type "Image?"

if (img == null) {
    Log.d(TAG, "Image is null!")
    return
}
// now img is smart-cast to the type Image (non-nullable)

Is it possible to create extension for that?

You won't be able to encapsulate the non-local return inside an extension function, but you could create an extension function for the logging, and then use a plain elvis operator:

fun YourReader.acquireLatestImageOrLog(): Image? {
    val img = acquireLatestImage()
    if (img == null) {
        Log.d(TAG, "Image is null")
    }
    return img
}

val img = reader.acquireLatestImageOrLog() ?: return

You could even go more general:

fun <T> T?.orLog(message: String): T? {
    if (this == null) {
        Log.d(TAG, message)
    }
    return this
}

val img = reader.acquireLatestImage().orLog("Image is null!") ?: return
Sign up to request clarification or add additional context in comments.

Comments

0

Here's one example using run:

val img: Image = reader.acquireLatestImage() ?: run {
    Log.d(TAG, "Image is null!")
    // You can place also more code in here -->
    // ...
    // <--
    return
}

Or if you simply want it in oneliner, you can place ; after Log.d(...):

val img: Image = reader.acquireLatestImage() ?: run { Log.d(TAG, "Image is null!"); return }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.