Using null type annotations in practice Till Brychcy, Mercateo - - PowerPoint PPT Presentation

using null type annotations in practice
SMART_READER_LITE
LIVE PREVIEW

Using null type annotations in practice Till Brychcy, Mercateo - - PowerPoint PPT Presentation

Using null type annotations in practice Till Brychcy, Mercateo EclipseCon Europe, 2017 What they are, why and when to use them @Nullable vs. java.util.Optional Configuration choices Switching from declaration annotations to type


slide-1
SLIDE 1

Using null type annotations in practice

Till Brychcy, Mercateo

EclipseCon Europe, 2017

slide-2
SLIDE 2

EclipseCon Europe, 2017

  • What they are, why and when to use them
  • @Nullable vs. java.util.Optional
  • Configuration choices
  • Switching from declaration annotations to type annotations
  • How to get warning free code - Code Patterns and Antipatterns
  • Free Type variables and extends @Nullable
  • Arrays
  • Improvements released with Oxygen
  • Preview to Photon
slide-3
SLIDE 3
slide-4
SLIDE 4

EclipseCon Europe, 2017

Some Statistics

  • First commit: Sep 18 2002
  • First commit with null annotations: Jul 2 2012
  • Switched to java 8 and null type annotations: Apr 15 2015
  • Sample size: 7866 java files (approx. 20% of total code base)
  • 3025 files use @NonNullByDefault (no package annotations)
  • 10910 @Nullable annotation in 2175 files
  • 1970 @NonNull annotations in 709 files
  • 1070 @SuppressWarnings(„null")
slide-5
SLIDE 5

EclipseCon Europe, 2017

Before null annotations

/** * * @param catalogID * @param groupID * @param searchSpec * (may be null) * @param minIndex * @param maxIndex * @param sortBy * @param sortAscending * @return String */

slide-6
SLIDE 6

EclipseCon Europe, 2017

Null annotation advantages

  • One thing less to worry about
  • Code that is easier to understand, change and debug
  • No more NullPointerException
  • Fewer other bugs
slide-7
SLIDE 7

EclipseCon Europe, 2017

Null annotations disadvantages

  • Compiler sometimes needs help: Avoid some code patterns
  • Syntax a bit ugly
slide-8
SLIDE 8

EclipseCon Europe, 2017

Newer languages: nice syntax

  • kotlin
  • var x: String? = null;
  • swift
  • var x: String? = nil;
  • c# (announced for version 8)
  • string? x = null;
slide-9
SLIDE 9

EclipseCon Europe, 2017

Null Annotations

  • Normal use: @NonNullByDefault + @Nullable
  • @NonNull: only for type parameters and during migration
slide-10
SLIDE 10

EclipseCon Europe, 2017

Declaration vs. type annotations

  • Declaration annotation:

@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE }) public @interface Nullable { }

  • Type annotation:

@Target({ TYPE_USE }) public @interface Nullable { }

  • "Mixed" annotations are allowed by Java, but bad as null annotations:

@Target({ TYPE_USE, FIELD, METHOD, PARAMETER, LOCAL_VARIABLE }) public @interface Nullable { }

slide-11
SLIDE 11

EclipseCon Europe, 2017

Declaration annotation usages

@NonNullByDefault public class Example { @Nullable String field; @Nullable String add(@Nullable String arg1, List<String> list, @Nullable String[] array) { // just to show the syntax (local variables usually don't need annotations) @Nullable String local = arg1; list.add(null); // is this OK? if(array != null) { array[0] = null; // or this? } return local; } }

slide-12
SLIDE 12

EclipseCon Europe, 2017

Equivalent with type annotations

@NonNullByDefault({ FIELD, PARAMETER, RETURN_TYPE }) public class Example { @Nullable String field; @Nullable String add(@Nullable String arg1, List<String> list, String @Nullable [] array) { // just to show the syntax (local variables usually don't need annotations) @Nullable String local = arg1; list.add(null); // is this OK? if(array != null) { array[0] = null; // or this? } return local; } }

slide-13
SLIDE 13

EclipseCon Europe, 2017

Completely annotated

@NonNullByDefault({ FIELD, PARAMETER, RETURN_TYPE, ARRAY_CONTENTS, TYPE_ARGUMENT }) public class Example { @Nullable String field; @Nullable String add(@Nullable String arg1, List<@Nullable String> list, @Nullable String @Nullable [] array) { // just to show the syntax (local variables usually don't need annotations) @Nullable String local = arg1; list.add(null); // OK! if (array != null) { array[0] = null; // OK! } return local; } }

slide-14
SLIDE 14

EclipseCon Europe, 2017

@NonNullByDefault defaults

public enum DefaultLocation { PARAMETER, RETURN_TYPE, FIELD, TYPE_PARAMETER, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS } @Target({ ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE }) public @interface NonNullByDefault { DefaultLocation[] value() default { DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.FIELD, DefaultLocation.TYPE_ARGUMENT, DefaultLocation.TYPE_BOUND }; }

slide-15
SLIDE 15

EclipseCon Europe, 2017

Type bounds: "extends"

  • Think in types:

Object x = ""; // OK

String s = new Object(); // error @Nullable String s1 = ""; // OK @NonNull String s2 = null; // error

⇒"String extends Object" corresponds to "@NonNull String extends @Nullable String"

  • So:

"@NonNull String extends @NonNull Object" "@NonNull String extends @Nullable Object" "@Nullable String extends @Nullable Object"

slide-16
SLIDE 16

EclipseCon Europe, 2017

Free Type variables and extends @Nullable

With standard @NonNullByDefault:

  • class List<T> {…} T can be @Nullable or @NonNull
  • user chooses List<@Nullable Integer> or List<String> (=List<@NonNull String>)
  • class NumberList<T extends Number>{…} T must be @NonNull
  • same as class NumberList<T extends @NonNull Number>
  • same as NumberList<@NonNull T>
  • class NumberList<@Nullable Number>: T must be @Nullable
  • class NumberList<T extends @Nullable Number>: T can be @Nullable or @NonNull
slide-17
SLIDE 17

EclipseCon Europe, 2017

Principles

  • Warning free workspace
  • @NonNullByDefault in new code
  • Add @NonNullByDefault to existing code with other changes
  • If necessary, annotate related code
  • @SuppressWarnings("null") is OK in certain situations
  • Must be easy to use with maven (now: no maven settings at all, IDE-only)
slide-18
SLIDE 18

EclipseCon Europe, 2017

@SuppressWarnings

  • Generated code: hashCode & equals
  • stream.filter(x->x != null).
  • optional.orElse(null)
  • Tests (still add @NonNullByDefault)
  • Overrides for which we don’t want to add external annotations
slide-19
SLIDE 19

EclipseCon Europe, 2017

Configuration choices

  • Enable annotations based analysis in workspace settings (not project)
  • File template with @NonNullByDefault
  • DefaultLocation as favorites
  • External annotations added to the JDK in Workspace
  • Hide INFO in problems view
slide-20
SLIDE 20

EclipseCon Europe, 2017

slide-21
SLIDE 21

EclipseCon Europe, 2017

slide-22
SLIDE 22

EclipseCon Europe, 2017

Why custom annotations

  • Different defaults for @NonNullByDefault (e.g. exclude FIELD)
  • @Retention(RUNTIME) for testing framework
  • Easier to accept for users of other IDEs
slide-23
SLIDE 23

EclipseCon Europe, 2017

Workspace-wide EEA for the JDK

slide-24
SLIDE 24

EclipseCon Europe, 2017

slide-25
SLIDE 25

EclipseCon Europe, 2017

slide-26
SLIDE 26

EclipseCon Europe, 2017

Challenges when switching to type annotations

  • Syntax for qualified names java.io.@Nullable File file (easy to fix)
  • Syntax for Arrays (we used some regular expressions)
  • Generics:
  • Map.get() (configure external annotations)
  • Generics that take a .class literal
slide-27
SLIDE 27

EclipseCon Europe, 2017

Arrays

  • @Nullable String @NonNull [] x;
  • Problem during migration from declaration annotations
  • Problem: new @NonNull String[10] contains nulls
  • methods that don't care about nullness about array contents

<T extends @Nullable String> @NonNull String concat(T[] strings)

slide-28
SLIDE 28

EclipseCon Europe, 2017

Observed null parameter handling

  • 1. Don't think about it, let method caller guess
  • 2. if(param==null) return null
  • 3. Objects.requireNonNull(param)
  • 4. try{…}catch(Exception e){…}
  • 5. Assume nonnull, use javadoc for nullable
slide-29
SLIDE 29

EclipseCon Europe, 2017

Some Antipatterns

  • No correlation analysis
  • boolean b=(x != null); if(b) {x.someMethod()}
  • int length = array == null ? 0 : array.length; for (int i = 0; i < length; i++)…
  • No intraprocedural analysis:
  • init(…)-methods in constructor
  • if(isValid(x)) { x.something() }

boolean isValid(Some x) {return x != null && …}

  • Event callbacks without context: e.g., org.xml.sax.ContentHandler
  • Struts form beans
  • some builder patterns
slide-30
SLIDE 30

EclipseCon Europe, 2017

Some Good Patterns

  • Empty string / collection / NOP-implementation instead of null
  • final fields or even completely immutable fields
  • Avoid null literals except to define class specific NULL constants
slide-31
SLIDE 31

EclipseCon Europe, 2017

@Nullable vs. java.util.Optional

  • Two solutions for the same topic.
  • "Don't care": Convenient mapping to other Optionals
  • Code with Optional gets reliable with @NonNullByDefault, so use both
  • Use Optional only for return values
  • Optional is not Serializable
  • @Nullable better when overriding methods
  • Problem Optional#orElse (Guava had: #orNull)
slide-32
SLIDE 32

EclipseCon Europe, 2017

Improvements in Oxygen

  • 54 bug fixes and enhancements related to null analysis and null annotations
  • Quick Fix to move type annotations
  • @NonNullByDefault
  • DefaultLocation.ARRAY_CONTENTS implemented
  • @Target(ElementType.FIELD) implemented
  • @Target(ElementType.LOCAL_VARIABLE) implemented
  • No warning for "T extends @Nullable String"
  • Many quick assists avoid creating redundant @NonNull
slide-33
SLIDE 33

EclipseCon Europe, 2017

  • extract local variable
  • create local variable for missing symbol
  • create field for missing symbol
  • create parameter for missing symbol
  • override method
  • create method (parameters, details of

return type)

  • change method: (add parameter)
  • change method: (change parameter)
  • assign expression to new local

variable

  • create constructor using fields
  • generate delegate methods
  • create method
  • type change and signature change
  • introduce parameter object
  • extract class
slide-34
SLIDE 34

EclipseCon Europe, 2017

Preview to Photon

  • Quick fix to add @NonNullByDefault to package
  • Java 9: @NonNullByDefault in module-info.java
  • Maybe support 3rd-party NonNullByDefault variations

(TypeQualifierDefault)

  • More quick assists that avoid creating redundant @NonNull