You are currently viewing Idiomatic !!

Idiomatic !!

A recent post complaining about ?. and suggesting !! as a replacement was roundly rejected in the comments. No complaints from me there, but it got me thinking about when it is OK to use !! in our code. Here’s what I could come up with. I don’t code for a living, so I’m much more interested in knowing why I’m wrong rather than being right.

I. In testing. Several commenters in the aforementioned post stated that this is the only idiomatic usage of !! in code. I (gently) disagree; otherwise I wouldn’t be posting. But it makes sense to me to use them here. You have unusually strong control over the input; it’s not user-facing; and failing fast is good.

II. Working with third-party libraries. I snagged this example from Baeldung:

“For example, let’s say we’re using bean validation annotations on a Spring DTO:

data class NewUserDto( @get:NotNull val username: String? = null ) 

Spring automatically applies the validation rules to the request data. Therefore, after the validation is done, we’re sure that the username is not null:

val request: NewUserDto = // from controller userService.registerUser(request.username!!) 

Even though we do know the value is not null from the context, the Kotlin compiler has no way to infer this information. So, we have to use the !! operator here.”

III. Where the code is built around it. I’m not so sure about this one. Here’s an example from the core library (the Kotlin Regex class):

public actual fun replace(input: CharSequence, transform: (MatchResult) -> CharSequence): String { var match: MatchResult? = find(input) ?: return input.toString() var lastStart = 0 val length = input.length val sb = StringBuilder(length) do { val foundMatch = match!! sb.append(input, lastStart, foundMatch.range.start) sb.append(transform(foundMatch)) lastStart = foundMatch.range.endInclusive + 1 match = foundMatch.next() } while (lastStart < length && match != null) if (lastStart < length) { sb.append(input, lastStart, length) } return sb.toString() } 

Here, foundMatch can never be null because it’s only defined in a loop where the underlying match variable has already been null-checked. Of course, one could try to find some cromulent ?: default value or break the loop, but 1) it seems silly to put in all this failsafe code for something that should not fail; and 2) this seems like a good way to let in a subtle bug that might not be noticed for a long time, in part of the API that is commonly used for validation. Perhaps it makes sense to let it fail fast and even catastrophically in production code?

submitted by /u/pdxbuckets
[link] [comments]