From what I remember, the use of the double bang i...
# codingconventions
d
From what I remember, the use of the double bang is discouraged. However, I find myself using it a lot to intentionally throw an NPE when something really shouldn’t be null. Example would be a SQL query that should either return no rows or a row with a single column with a non null value. There’s no reason the column should ever be null unless someone changed the DDL of the table at which point the NPE essentially just informs them they have more work to do with such a change. Anyone else experiencing this?
t
In those cases I prefer a
?: error("some useful information about why it failed")
1
s
colin beat me to it
an NPE doesn’t intrinsically tell you anything more specific than “you’re trying to dereference a null pointer dummy”
calling
error()
with a useful message is way more helpful (and easier to search for in the codebase)
d
Yeah, I guess that’s true.
t
The only time I’ll use
!!
is when it absolutely cannot be null but I’m interoping with something that doesn’t let the compiler know
d
I guess a lot of these use cases are the result of an inline function I created to make getting values from a ResultSet easier. Example:
val timestamp: LocalDate = rs["create_date"]!!
though usually the type inference comes from a method/constructor parameter
but I think I just realized that maybe I wrote that function incorrectly…
s
mazel tov 🎉
d
Copy code
inline operator fun <reified T> ResultSet.get(columnLabel: CharSequence): T? = getObject(columnLabel.toString(), T::class.java)
return type should probably just be
T
right?
s
that makes sense, assuming you’re intending for folks to parameterize the call properly if the field is intentionally nullable
d
right
Actually, I think there was something peculiar that required that due to the way getObject handles primitives…
Or the interop when inferring types from java primitives.
yeah.. so you could infer from an
int
and easily use
rs["id"] ?: 0
SO… having remembered that, it kind of forces me to use !! to get non-nullable types out of it. I feel like putting ?: error(“…”) after each call to it would quickly get unsightly.
Copy code
Message(
    rs["create_date"] ?: error("create_date is null"),
    rs["body"] ?: error("body is null"),
    rs["subject"],
    rs["message_id"] ?: error("message_id is null")
)
Would you still prefer an elvis error in this case?
s
feel like you could define a different getter if you’re not super attached to the operator fun
d
Specifically for the cases with numbers?
s
well idk about the types in question here but you could maybe have a
get()
and a
getNullable()
or a
getThrowing()
if you actually want that to be the exceptional behavior
your call
d
The operator part of it was pretty nice I think.
Copy code
Message(
    rs["create_date"]!!,
    rs["body"]!!,
    rs["subject"],
    rs["message_id"]!!
)
s
Copy code
Message(
  rs["create_date"],  // these will throw
  rs["body"],
  rs.getNullable("subject"),  // this will not
  rs["message_id"]
)
it’s perhaps not as nice-looking but is probably easier to debug
ultimately it’s up to you what kind of API you wanna publish
I see where you’re coming from and don’t necessarily disagree
d
I could abuse the
invoke
operator for the other case… but that’s probably not a good idea.
s
it’s not great, would probably surprise folks (though the scala people might not hate it lol)
d
Maybe I could just create an extra extension function for the ones that have exceptional cases… I’ll have to fiddle around with that.
rs["id"] { 0 }
overload get with a block param
Less intuitive though I think
s
idk, providing a default is pretty different from NPEing if you’ve reached an illegal state
d
You know… to be fair, if that function didn’t return
T?
it would still NPE if the result of
getObject
ended up being null
Which would occur if the programmer made a mistake or if the DDL changed.
s
As an alternative to the
?: error("message")
approach, there's also
checkNotNull
and
requireNotNull
, which might be of interest. I tend to favor those over
!!
, at least, and usually also
checkNotNull(foo) { "message" }
over
foo ?: error("message")
.
t
For things like this we have an extension method to make things clearer, so something like:
Copy code
Message(
    rs["create_date"].orDie(),
    rs["body"].orDie(),
    rs["subject"],
    rs["message_id"].orDie()
)
m
Although not pretty, I prefer
checkNull
or
requireNull
calls. And function returns non-null. More explicit than !!, and throws
Illegal
exception rather than NPE. I find that more realistic, as
!!
should only be used in cases where the compiler can't figure it out, but you KNOW it can't/won't be null at that point.