how can i make this conditional expression more el...
# getting-started
s
how can i make this conditional expression more elegantly with out `null == foo`?
Copy code
if (null == foo || foo?.token == bar.token) { doSomeThing() }
c
something like
foo?.takeIf { it.token == bar.token }?.let { doSomething() }
j
Not sure that's better readability wise, especially since we apparently don't need
foo
. The initial code is actually very clear
c
agree, the readability isn’t wonderful.
j
Also, it's actually not equivalent,
doSomething
should be run if
foo
is null
👍 1
s
I just think like @Chris Lee but it is not very well
c
I tend to not use many
if
statements in my code, prefering
when
for clarity. Something like this reads better:
Copy code
when {
  	foo == null -> doSomething()
  	foo.token == bar.token -> doSomething()
  }
the op asked to remove
foo == null
which isn’t possible given the logic of that statement.
j
@supra I wouldn't usually go for
.let
if I'm making a side-effect like here. I find that
if
statements are better suited in this case.
m
In the context, you're asking if fieldA == null or fieldB == fieldC the fact that fieldB is a subfield of a nonnullable fieldA doesn't matter for the fact that you are asking two seperate questions. I know that ?. and all implies a neat trick for nullable fields, but in this case it would be clearer to keep the two predicates as seperate entities
2
j
I agree with Michael. Anyway I think this discussion is not very objective and leans towards personal taste. I would simply write your initial expression like this (flipping the null check, and removing the safe call operator if possible):
Copy code
if (foo == null || foo.token == bar.token) {
    doSomeThing()
}
4
It would be even better if you could make foo non-nullable, and rather express what null means for it in a more type-safe way, which would allow to clarify this condition
s
I am agree with @Joffrey and @Michael de Kaste, but in fact, the
null == foo
is very likely to be accidentally deleted by colleagues.
j
Why so?
s
@Joffrey cause by poor code review😂
j
I mean why would they want to delete this check?
m
tests should cover this 😛
💯 3
deleting foo == null somewhere should fail a test somewhere
3
we do have in our own project, the following extension functions:
Copy code
inline fun <T> Boolean.ifTrue(block: () -> T): T? = if (this) block() else null
inline fun <T> Boolean.ifFalse(block: () -> T): T? = if (this) null else block()
inline fun <T> Boolean?.ifTrueOrNull(block: () -> T): T? = (this != false).ifTrue { block() }
inline fun <T> Boolean?.ifFalseOrNull(block: () -> T): T? = (this != true).ifTrue { block() }
so you can write something like:
Copy code
foo?.token?.equals(bar.token).ifTrueOrNull{ doSomething() }
if that soothes your heart
but then again you can do:
Copy code
if(foo?.token?.equals(bar.token) ?: true){ doSomething() }
as well
👍 1
s
SO GREAT!thanks a lot with @Michael de Kaste,and also thanks @Joffrey @Chris Lee
👍 1
j
I would really not recommend using elvis this way, it completely obfuscates the initial idea
s
and i will also accept with null checking for simple and clean
j
If you have 2 distinct situations (e.g. NOT authenticated OR authenticated with the same token), the condition should express them both
s
agree,the more elegantly solution i think is keep the null checking and cover this by unit testing
j
Also note that the safe call version above can mean something different if the
token
property of
foo
is itself nullable
s
thanks @Joffrey i have already noticed this. the
token
is non null value
👌 1
w
If you don't want your colleagues to remove the null check. You have even more reason to remove the unnecessary safe call.
foo.token
won't even compile if someone removes the
foo == null
check. I think that the proposed if statement is already complete. If I were to think of a way to describe something like this in a utility method, I would make it something like:
Copy code
if (foo.isNullOr { it.token == bar.token }) { doSomething() }
They both read the same: "if foo is null or it's token equals bar's token" vs your "if foo is null or *foo*'s token equals bar's token". I do not say that this is objectively more readable at all. I just wanted to show what I personally use in case of chained calls such as
application.service<UserRepository>().isNullOr { it.isInactive }
. I think it's more explicit and it's easier to evaluate while debugging.
👍 1
j
Copy code
if(foo?.token?.equals(bar.token) != false){ doSomething() }
seems elegant to me. It reads doSomething unless we have a clear discrepancy in the tokens between foo and bar
s
btw. you no longer need the "?" in "foo?.token". The compiler will have smartcasted foo to be not-null at this point because it already cleared "null == foo", so
Copy code
if (null == foo || foo.token == bar.token) { doSomeThing() }
will work.
j
Depends on what
foo
is, though. If it's a mutable property, that is not true
1
s
@Joffrey correct, but in this case it'd be a good idea to make a local copy (or synchronized access) anyway.
👍 1