Nullability in Java

Remi Forax forax at univ-mlv.fr
Wed Oct 2 07:16:29 UTC 2024


Hello,
i would like a propose a semantics adding a null-analysis to Java.

Principles
- 1/ the compiler should emit a warning only if a NullPointerException will occur if the value is null
- 2/ the syntax should explicitly indicate where a NPE can occur
- 3/ the compiler should be smart enough to avoid bothering users when a NPE can not occur (by example: after a "if (a != null)" a should be considered as non-null)

Nice to have
- while the internal representation is a 3 states (String!, String? and String), it should be cool if users can only denote two states to simplify the action a user can do
- the change of the JLS should be confined to a section

Proposal:
- on field, methods, casts, arrays and type arguments, the null-analysis marker '!' can be used to denote a non-null type.
  A NPE will be raised
  - if a null value is stored in such field, array,
  - if a null value is casted to a non-null type,
  - if a null value is pass as argument of a method parameter typed with a non-null type

  Example:
    class Foo {
      String! s;
      String![] array = ...;

      void m(String! a) {} // NPE

      void main() {
        s = aMethodThatReturnsNull();  // NPE
        array[0] = aMethodThatReturnsNull();  // NPE
        var o = (String!) aMethodThatReturnsNull();  // NPE
        m(aMethodThatReturnsNull());   
      }
    }

- on field, methods, casts, arrays and type arguments, the null-analysis marker'?' can be used to denote a nullable type.

- Inside methods, local variable declaration are NOT annotated with nullability marker, the nullability is inferred,
  so the analysis
   - can be smarter than the current type-checking, especially the nullability of a local variable can change depending on the control flow
   - the analysis can be separated from the type checking in the spec (but not in the compiler)
   - even if the proposed semantics is not non-null by default, a user will not have to annotate a lot of locus, mostly only fields and methods.

  Example:
   class Bar {
     String! aMethodThatCannotReturnsNull() { ... }
     String? aMethodThatReturnsNull() { return null; }

     void main() {
       String s = aMethodThatCannotReturnsNull();  // the type of 's' is inferred as String!
       String s2 = aMethodThatReturnsNull();       // the type of 's2' is inferred as String?
       if (s2 != null) {
         // here the type of s2 is String!
       }
     }
   } 

- on fields and methods, every type wich is not a ! (a non-null type) is a ? (a nullable type)
  if there is already a '?' or '!' in the same compilation unit (same file).
  Having a '?' or '!' somewhere acts as an opt-in to the null-analysis mechanism, it guarantee that
   - until you opt-in, no warnings can be raised
   - if you opt-in, the user model has only two denotable kinds of nullability, String! and String?, the latter can written String
  
- A type variable is nullable by default and propagate the nullability information if a user has opt-in to the null-analysis.
  At declaration site, E is equivalent to E?, which really means E extends Object?
  
  Example:
   class Foo<E?> {  // a '?' is present so the user as opt-in to E propagating the nullability information
     void m(E! e) { }  // m() can not be called with null
     void m2(E e) { }  // equivalent to void m2(E?)  
   )  

- As discussed previously, a field with a non-null type must be initialized in the constructor before the call to the super constructor call.

- @SuppressWarnings("null-analysis") allows to suppress any null warnings so even if a user has opt-in to the null analysis it can still
  incrementally had the null marker later. This also allows add null markers on public methods (on the API) without having any warnings
  in the implementation.

regards,
Rémi



More information about the valhalla-spec-experts mailing list