Hi all, I am trying to do something and I want to ...
# announcements
i
Hi all, I am trying to do something and I want to check what would be a idiomatic way to do it: I will simplify the problem here: Let's say I have the following function
Copy code
fun getSomeString(): String?
and I have a function that wraps a String (not String?) in some Response
Copy code
fun getResponse(msg: String): Response
then I have a third function in which I need to return a Response and I am calling the getSomeString, if it returns a String I need to feed it to getResponse, but if it returns null I need to return some ErrorResponse object. Now, I wanted to make this a one liner, but I'm having problems with thinking how to do it It would be great if I could do something like this:
Copy code
fun GetResponse: Response = 
  when(getSomeString()) {
    is String message -> getResponse(message)
    else -> ErrorResponse()
  }
but I can't do this is String message. In my real use-case this is not a String but a data class with a lot of fields, and I could match on all those fields and then recreate the object on the right side of -> but that just doesn't seem right. How would this usually be done in Kotlin? I am used to some other languages where this approach of naming the String would be done in is String message ->...
b
From what I understand from your question, you want to make a safe call:
Copy code
getSomeString()?.let { getResponse(it)} ?: ErrorResponse()
j
You can use the elvis operator or a
?.let
construction if you are only worried about the null check.
i
Hi, thanks both for the answer. I come from languages with strong pattern matching so I immediately went in that direction with when
I'll do it like that. Do you happen to know if it can somehow be done with when?
a
you can declare the variable in the
when
expression, that is
when (val message = getSomeString())
, and then the
is String
clause will permit a smart cast
a
You can revert
when
statement
Copy code
fun GetResponse: Response = 
  when(val message = getSomeString()) {
    null -> ErrorResponse()
    else -> getResponse(message)
  }
This should smartcast
message
in else case
i
thanks for the answer, I knew something like that should work!
a
But personally for
null else
case I would prefer to use
?.let{ } ?:
i
yeah, it seems like it is more idiomatic in kotlin so i'll go with that, just wanted to know how when handles these situations
a
or even better would be
if else
statement
j
Alternatively wrap the return in an Option<>
a
Copy code
fun GetResponse: Response { 
  val message = getSomeString()
  return if(message != null) {
    getResponse(message)
  } else { 
    ErrorResponse()
  }
}
k
?.let{ }?:
is an anti-pattern imho. The docs tells us that we should prefer
if else
but I like that you can assign inside a
when
, something that isn't possible with a
if
a
Yes, thats why I prefer
if else
over
when
then possible.
When
could generate weird output in bitecode. While
if else
is straight forward.
k
I would expect a
when
with a single case and a an
else
to become converted to a simple
if
.
a
totally agree 😄
i
is this something people debate on or is
?.let{} ?:
not considered idiomatic code in general? would you see it in popular libraries and so? I agree that if/else solution is easy to read, but I wanted to see how you would to this with
fun ... =
instead of
fun ... : <type> {return x}
as I find the former syntax to be nicer. That's why I like the when solution, as it is easy to read and the problem can be solved as a single expression
a
I would still prefer to use second variant with
if else
since I will be sure in what will be generated in bytecode. But if I would not care about that, I would prefer to use
when
over
?.let
k
?.let{} ?:
is not semantically equivalent with
if(it != null) else
Could cause bugs if a different function decides to have a nullable return.
p
Couple of other things you might like to play around with - especially if this is repeated code - Factory method
Copy code
fun getResponse() = Response.from(getSomeString())
where I wrapped the
if / else
into the response class
Copy code
class Response(private val someData: String) {
    companion object {
        fun from(data: String?) = if (data == null)
            ErrorResponse()
        else
            Response(data)
    }
}
Extension Method
Copy code
fun getResponse() = getSomeString().toResponse()
and then pulled the
if / else
into an extension method
Copy code
fun String?.toResponse() : Response = if (this == null)
    ErrorResponse()
else
    Response(this)