PROPOSALS for nullable and non-nullable references and graceful null handling
MOTIVATIONS
Nullable and non-null
You don't have to check for null when it is absolutely sure it cannot be null.
Most of the time, it shouldn't be null.
In most cases, fields, parameters, and variables are not expected to be null.
Fields may temporarily be null before the constructor has initialized the fields. This state is only transient.
Sometimes, it might be null.
Some fields may be null, for example in the case of lazy loading or lazy computation.
Null may also represent the 'not-assigned' value for an optional field.
Typing a reference as non-null reference should require no additional effort.
Typing a reference as nullable should be explicit and non-default. It should require minimal effort in terms of typing, but should be intentional.
Gracefull null handling
Dereferencing a nullable reference should not require boiler plate code in simple cases.
Graceful null handling is especially desired in the presentation layer.
This may be a personal view, but I see overuse of gracefull null handling in business layer as a serious anti-pattern.
It leads to very late failure, and difficult to diagnose bugs. The code fails much later after the unexpected null has appeared. I call that the 'null tunnel'.
General overuse of graceful null handling leads to code that do not fail when it doesn't work as expected.
This kind of code do not comply with the 'fail early' principle.
Many UI and web frameworks, such as JSF and JSP EL (expression language), have been providing gracefull handling of null for a long time.
This is especially useful when chaining properties such as :
customer.address.city
Null Object Pattern
The null object pattern is very difficult to understand for a vast majority of developpers.
I don't see that as a desirable feature, but it may reflect only my personal opinion).
NULLABLE AND NON-NULLABLE REFERENCES
All references (fields, parameters, and variables) are typed as non-null by default.
Typing as nullable is performed with a specific syntax.
I see two possible syntaxes :
- a 'nullable' keyword
- a '?' symbol posfixing the reference type
The '?' symbol would be much more consistent with the proposed null-safe operator '?.' and is also much shorter to type.
// Typed as non-null Customer references
Customer customer, otherCustomer;
// Typed as nullable Customer reference
nullable Customer nullableCustomer, otherNullableCustomer;
// Typed as nullable Customer reference (alternative syntax)
Customer? nullableCustomer, otherNullableCustomer;
// ---------- Assignment to non-null reference ----------
// Should NOT compile. CANNOT assign null to non-null reference
customer = null;
//Should compile. Can assign non-null reference to non-null reference
customer = otherCustomer;
// Should NOT compile. CANNOT assign nullable reference to non-null reference
customer = nullableCustomer;
// ---------- Derefencing non-null reference (calling method, accessing field or property) ----------
// Should compile. Can derefence non-null reference without checking null
customer.addOrder(order);
// Should NOT compile (or is it a WARNING). Useless to check for null on non-null reference
// (for general case, compiler should use control flow analysis to check this)
if (customer != null) {
customer.addOrder(order);
}
// Should NOT compile (or is it a WARNING). Useless to check for null on non-null reference
customer?.addOrder(order);
// ---------- Assignment to nullable reference ----------
//Should compile. Can assign null to nullable reference
nullableCustomer = null;
//Should compile. Can assign nullable reference to nullable reference
nullableCustomer = otherNullableCustomer;
//Should compile. Can assign non-null reference to nullable reference
nullableCustomer = customer;
// ---------- Derefencing nullable reference (calling method, accessing field or property) ----------
// Should NOT compile. CANNOT derefence null reference without checking null
nullableCustomer.addOrder(order);
// Should compile. Can dereference a null reference, when it has been checked for null
// (for general case, compiler should use control flow analysis to check this)
if (nullableCustomer != null) {
nullableCustomer.addOrder(order);
}
// Should compile. Can dereference a non-null reference, when using null safe derefencing operator (?.)
nullableCustomer?.addOrder(order);
GRACEFULL NULL HANDLINGThe '?.' (AKA elvis) operator allows to handle nullable references gracefully, without boilerplate.
Customer? nullableCustomer = null;
// Do nothing when calling a method on null reference
nullableCustomer?.addOrder(order);
// Do nothing and return null when calling a method with a result on null reference (even if return type is non-null ?)
nullableCustomer?.getPastOrders();
// Non null property accesse on null reference returns null (even if property type is non-null ?)
nullableCustomer?.firstName
WARNING : The behaviour of the '?.' should be much further specified