The billion dollar mistake has been committed already. No going back. But it is not necessary to keep repeating it. Oh, I am talking about the infamous null
.
A real solution to avert the issues surrounding null
is to completely avoid using it in your code, and replace direct use of null
with some sort of nullable container, such as the Option[A]
in Scala. You could go even as far and enforce not to use null
anywhere in your code via Scalastyle or other linters.
In Scala, it is pretty common to pass and return1 Option
. For instance …
case class Email(
subject: String,
body: String,
attachment: Option[Attachment]
)
object Email {
def apply(subject: String, body: String): Email =
new Email(subject, string, attachment = None)
}
In the above code, attachment
may optionally be passed to create an instance of Email
. Another way, a bad one, to achieve the same with sufficient safety around null
is:
// This is bad! Don't do it!
class Email(
val subject: String,
val body: String,
attached: Attachment
) {
val attachment: Option[Attachment] = Option(attached)
}
// calling code
new Email("hello", "world", null)
The above code does not use Option
but directly use null
, which is wrapped with an Option
inside the Email class. While this provides safety net around null
, it is just a bad way.
Introducing null
in one place potentially causes loss in the strictness of types. The null
may be direct value as shown in the example above. Or a runtime value passed down via chain of calls to a variable passed to Email
constructor, which makes it harder to reason about. Besides, did you notice I had to change the original case class
to a regular class
2?
Let us switch gears. Java too has a nullable container - the infamous3 Optional<T>
.
In the Java realm, returning Optional
from methods is acceptable. Passing Optional
as parameters is not.
IntelliJ warns such cases via its inspections.
While you can suppress inspections at your will, that is the not point. It is recommended not to pass Optional
as parameters. Even by Java experts.
I was not able to find a compelling reason behind such a recommendation, which is in contrast to Scala. Almost every link or material I came across was about to disabling4 the inspection to silence IntelliJ from throwing the warning.
However, I came across a couple of things where were better out of the lot. But not compelling.
- In the case when there are multiple
Optional
parameters, the calling code would look clumsy. Meh! Real production Java code is ugly 🤷♂️ because Java syntax is verbose and noisy. I don’t buy that multipleOptional
parameters is going to make uglier. - Using
Optional
parameters introduces conditional logic in the method. Of course, it should. Wouldn’t there be conditional logic or guards if we were dealing withnull
instead ofOptional
? OnlyOptional
makes it disciplined. You wouldn’t use the parameter in question blindly without null check, would you? Optional
is relatively expensive. This is a hard sell unless one can prove thatOptional
in the particular case is the bottleneck. In the grand scheme of things, the cost ofOptional
should be negligible. Unless it can be proven it is not negligible.- … although I buy the point here. If you are using
Optional
, typically you would use its methods that takes lambdas or such, which drags in some extra allocations. Again, should not be a problem unless it is proven to be a problem.
- … although I buy the point here. If you are using
- Finally, what if
null
itself is passed the value for theOptional
parameter? Duh! I am not denying this is a legit case although I see this as a problem of culture.- In Scala and other functional languages, there is a clear distinction in practice and convention between
null
andOption
. I am yet to encounter a case where a piece of code dealing withOption
received anull
. For that matter, I am yet to come across code that was working with aString
and encounterednull
. If aString
were to be optional, it would have been anOption[String]
. Validations at the highest levels may even wrap empty or blankString
s into aNone
; depending on the case. I thought I would say that the above is practiced religiously. But no. It is just how Scala programmers, and generally speaking functional programmers think about it. It is sadly not the case with Java; at least mainstream5.
- In Scala and other functional languages, there is a clear distinction in practice and convention between
On a larger scale, I prefer to align with the community - practices, guidelines and conventions. And temper only specific things to cater to my taste/style. For a good reason. In the case of this warning about using Optional
parameters, I have mixed feelings. Neither do I want to disable the inspection in IntelliJ because IntelliJ team is way smarter than us, and I hope they added it for a reason. Nor do I want litter my code with SuppressWarnings
4.
Given that the IntelliJ inspection is a tooling aspect and general advice from elders, I might consider disabling it after all.
-
The
case class
fields are getters and hence are returningOption
. The constructor as written above is the argument. So, passing and returning. ↩︎ -
Similar handling is not possible using
case class
; at least not in a clean way. You might end up having redundant fields breaking invariant onattachment
. Or some unnecessarily convoluted code. ↩︎ -
Yes, infamous. Because it is poorly implemented. I might have to write about it in separate post. Let me not digress. ↩︎
-
In the worst case you have to suppress the warning, use
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
instead of disabling the inspection for all cases. ↩︎ ↩︎ -
There are definitely some elite / esoteric Java code out there that fall in the category of functional thinking, if not really written using functional techniques. ↩︎