Null Safety 🦺
Null safety means eliminating null pointer exceptions (NPEs) by detecting null references at compile time.
Benefits
- Find bugs early - Surface issues during compilation rather than in production
- Avoid crashes - Prevent NPE crashing programs
- More semantics - Distinguish null, empty, unset, default values
- Non-optional by default - Null must be explicitly declared
Default-ing to empty string
Defaulting to a value is common but to an empty string is often a scenario where we don’t see the forest for the trees. Sometimes for that case, we can address the root cause, sometimes defaulting is the pragmatic choice due to external constraints. However, it is important to always understand it before applying the workaround.
This “empty string” can be problematic for a few reasons:
-
Obscures meaning - An empty string can imply a value was populated but is empty, vs null clearly meaning absent/uninitialized.
-
Data integrity issues - An empty string can result in data being improperly stored vs nulls clearly representing missing values.
-
Type mismatch - Empty strings can lead to mismatches converting between string and numeric values.
-
Hides bugs - Errors like uninitialized values can be masked by empty strings rather than raising visibly like nulls.
-
Lack of semantics - Empty string overloads a display concept vs null having clear semantics of an absent value.
-
Boilerplate checks - Code must add extra null/empty checks when meaning is ambiguous.
In general, semantically it is better to represent absent values explicitly rather than try to overload empty strings to serve dual purposes. Nullity conveys different meaning than emptiness.
Implementation
Approach 1 - Value Wrapper
public class Order {
private long orderId;
private String giftText;
public Optional<String> getGiftText() {
return Optional.ofNullable(giftText);
}
}
Offering backward compatibility for languages like Java. It requires developers to implement it properly.
Approach 2 - Nullable Type
data class Order(private val orderId: Long, private val giftText: String?)
More recent languages don’t have the legacy baggage and can eliminate the issue by design.