$30 off During Our Annual Pro Sale. View Details »

Fixing a billion dollar mistake

Fixing a billion dollar mistake

A presentation regarding so called "Billion Dollar Mistake", which is introducing of Null Reference. Presentation covers different ways of handling that problem in Java and JVM projects. Talk was presented during Quality Meetup #15 in Katowice, Poland.

Piotr Wittchen

February 08, 2018
Tweet

More Decks by Piotr Wittchen

Other Decks in Programming

Transcript

  1. Fixing a billion
    dollar mi$take
    Piotr Wittchen
    1

    View Slide

  2. What is a billion dollar mistake?
    2

    View Slide

  3. null
    3

    View Slide

  4. A bit of history
    https://en.wikipedia.org/wiki/Tony_Hoare
    https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
    4

    View Slide

  5. Bad practices nowadays according to Uncle Bob
    Passing null
    Returning null
    5

    View Slide

  6. Passing null
    6

    View Slide

  7. What could happen if we pass null to our method?
    public void processData(String data) {
    data.contains("foo");
    }
    processData(null);
    NullPointerException
    7

    View Slide

  8. Null-checks in application code
    public void processData(String data) {
    if(data != null) {
    data.contains("foo");
    }
    }
    processData(null);
    8

    View Slide

  9. Null-checks in library API
    public void processData(String data) {
    if(data == null) {
    throw new NullPointerException("data is null!");
    }
    data.contains("foo");
    }
    9

    View Slide

  10. Objects.requireNonNull(...) method introduced in Java 7
    public void processData(String data) {
    Objects.requireNonNull(data, "data cannot be null");
    data.contains("foo");
    }
    10

    View Slide

  11. Guava and Preconditions
    public void processData(String data) {
    Preconditions.checkNotNull(data ,"data cannot be null");
    data.contains("foo");
    }
    11

    View Slide

  12. @Nonnull Annotation
    public void processData(@Nonnull String data) {
    data.contains("foo");
    }
    12

    View Slide

  13. Returning null
    13

    View Slide

  14. Dealing with collections
    public List createList() {
    List list = new ArrayList<>();
    ...
    if(canReturnList) {
    return list;
    }
    return null;
    }
    14

    View Slide

  15. Dealing with collections
    public List createList() {
    List list = new ArrayList<>();
    ...
    if(canReturnList) {
    return list;
    }
    return list;
    }
    15

    View Slide

  16. Dealing with collections
    public List createList() {
    List list = new ArrayList<>();
    ...
    if(canReturnList) {
    return list;
    }
    return new ArrayList<>();
    }
    16

    View Slide

  17. Null Object Pattern
    interface NullableObject {
    boolean isNull();
    Object getObject();
    }
    class RealObject implements NullableObject {
    @Override public boolean isNull() {
    return false;
    }
    @Override public Object getObject() {
    return new Object();
    }
    }
    17

    View Slide

  18. Null Object Pattern
    interface NullableObject {
    boolean isNull();
    Object getObject();
    }
    class NullObject implements NullableObject {
    @Override public boolean isNull() {
    return true;
    }
    @Override public Object getObject() {
    return null;
    }
    }
    18

    View Slide

  19. Null Object Pattern
    NullableObject object;
    Object returnedValue;
    if(!object.isNull()) {
    returnedValue = object.getObject();
    }
    19

    View Slide

  20. Optional type in Guava
    final Optional optional = Optional.of(new Object());
    if (optional.isPresent()) {
    Object notNull = optional.get();
    }
    20

    View Slide

  21. Optional type in Guava
    final Optional optional = Optional.absent();
    final boolean isPresent = optional.isPresent();
    assertThat(isPresent).isFalse();
    21

    View Slide

  22. Optional type in Guava
    final Optional optional = Optional.fromNullable(null);
    final boolean isPresent = optional.isPresent();
    assertThat(isPresent).isFalse();
    https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained
    22

    View Slide

  23. Optional type in Java 8
    Optional optional = Optional.of(new Object());
    if (optional.isPresent()) {
    Object notNull = optional.get();
    }
    This example is the same as Guava Optional
    23

    View Slide

  24. Optional type in Java 8
    Optional optional = Optional.empty();
    Object returnedValue = optional.orElse(StringUtils.EMPTY);
    assertThat(returnedValue).isInstanceOf(String.class);
    24

    View Slide

  25. Optional type in Java 8
    Optional optional = Optional.of(new Object());
    optional.ifPresent(object -> assertThat(object).isNotNull());
    25

    View Slide

  26. Optional type in Java 8
    final Optional optional = Optional.of(new Object());
    final String value = optional
    .map(o -> o.toString())
    .orElse(StringUtils.EMPTY);
    26

    View Slide

  27. Enhancements of Optional type in Java 9
    final Optional optional = Optional.of(new Object());
    optional.ifPresentOrElse(object -> assertThat(object).isNotNull(),
    Assert::fail);
    27

    View Slide

  28. Enhancements of Optional type in Java 9
    final Optional optional = Optional.empty();
    Optional newValue = optional.or(() -> Optional.of(new Object()));
    assertThat(newValue.isPresent()).isTrue();
    28

    View Slide

  29. Enhancements of Optional type in Java 9
    List> optionals;
    optionals
    .stream()
    .flatMap(Optional::stream)
    .collect(Collectors.toList());
    29

    View Slide

  30. We have even better Option (in Vavr)
    Option option = Option.of(new Object());
    boolean defined = option.isDefined();
    assertThat(defined).isTrue();
    30

    View Slide

  31. We have even better Option (in Vavr)
    Option option = Option.none();
    boolean defined = option.isDefined();
    assertThat(defined).isFalse();
    31

    View Slide

  32. We have even better Option (in Vavr)
    Option option = Option.of(null);
    boolean defined = option.isDefined();
    assertThat(defined).isFalse();
    32
    // and no exception is thrown here!

    View Slide

  33. We have even better Option (in Vavr)
    Option option = Option.of(new Object());
    Object returnedValue = option.getOrElse(() -> StringUtils.EMPTY);
    33

    View Slide

  34. We have even better Option (in Vavr)
    final Option option = Option.of(new Object());
    option.forEach(object -> {
    if (option.isDefined()) {
    // handle object here
    } else {
    // object does not exist
    }
    });
    34

    View Slide

  35. We have even better Option (in Vavr)
    final Option option = Option.of(new Object());
    option
    .peek(object -> /* handle object here*/)
    .onEmpty(() -> /* object does not exist*/);
    35

    View Slide

  36. We have even better Option (in Vavr)
    private Object getObject() {
    throw new NullPointerException("Surprise! There's no Object!");
    }
    final Object object = Try .of(this::getObject)
    .toOption()
    .getOrElse(new Object());
    assertThat(object).isNotNull();
    // and no exception is thrown here!
    36

    View Slide

  37. We have even better Option (in Vavr)
    Option option = Option.of("my string");
    String result = Match(option).of(
    Case($Some($()), String::toUpperCase),
    Case($None(), () -> StringUtils.EMPTY)
    );
    assertThat(result).isEqualTo("MY STRING");
    37

    View Slide

  38. Option type and Vavr
    References:
    ● http://www.vavr.io
    ● http://www.vavr.io/vavr-docs/#_option
    ● https://softwaremill.com/do-we-have-better-option-here
    ● https://dev.to/koenighotze/in-praise-of-vavrs-option
    38

    View Slide

  39. Optional won’t save us from NPEs
    Optional optional = Optional.of(new Object());
    ...
    39
    NullPointerException
    optional = null;
    optional.isPresent();

    View Slide

  40. Let’s talk about immutability
    Object object = new Object();
    object = null;
    object.toString();
    40
    NullPointerException!

    View Slide

  41. Let’s talk about immutability
    final Object object = new Object();
    object = null;
    object.toString();
    41
    Error: java: cannot assign a value to final variable object

    View Slide

  42. Let’s talk about immutability
    final Object object = new Object();
    object = null;
    object.toString();
    42
    Error: java: cannot assign a value to final variable object

    View Slide

  43. Let’s talk about immutability
    final Object object = new Object();
    object = null;
    object.toString();
    43
    Error: java: cannot assign a value to final variable object

    View Slide

  44. Let’s talk about immutability
    final Object object = new Object();
    object = null;
    object.toString();
    44
    Error: java: cannot assign a value to final variable object

    View Slide

  45. Let’s talk about immutability
    final Object object = new Object();
    object.toString();
    45
    Don’t modify the state of the data if you don’t have to

    View Slide

  46. Null safety in Kotlin
    value.getObject() // IntelliJ will highlight this as an error if there could be NPE!
    if (value != null) { // null-check
    value.getObject()
    }
    value?.getObject() // safe call
    val l: Int = if (b != null) b.length else -1
    val l = b?.length ?: -1; // Elvis operator
    value!!.getObject() // unsafe call - can produce NPE!
    46
    https://kotlinlang.org/docs/reference/null-safety.html

    View Slide

  47. Maybe type in RxJava
    47
    Maybe is lazy and asynchronous

    View Slide

  48. Maybe type in RxJava
    Maybe maybe = createMaybe();
    maybe
    .subscribeOn(Schedulers.io())
    .subscribe(object -> System.out.println(object.toString()));
    48

    View Slide

  49. Maybe type in RxJava
    Maybe maybe = createMaybe();
    maybe
    .subscribeOn(Schedulers.io())
    .subscribe(new MaybeObserver() {
    @Override public void onSubscribe(Disposable d) {
    }
    @Override public void onSuccess(Object o) {
    }
    @Override public void onError(Throwable e) {
    }
    @Override public void onComplete() {
    }
    });
    49

    View Slide

  50. Compile time analysis
    50

    View Slide

  51. Error Prone
    Error Prone is a static analysis tool for Java developed at
    Google that catches common programming mistakes at
    compile-time.
    https://github.com/google/error-prone
    51

    View Slide

  52. Null Away
    NullAway is a tool developed at Uber built as an Error Prone
    plugin to help eliminate NullPointerExceptions (NPEs) in
    your Java code.
    https://github.com/uber/NullAway
    52

    View Slide

  53. Null Away
    static void log(Object x) {
    System.out.println(x.toString());
    }
    static void foo() {
    log(null);
    }
    warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
    log(null);
    ^
    53

    View Slide

  54. Null Away
    Integration with Gradle-based Java project
    https://github.com/uber/NullAway#java-non-android
    Integration with Gradle-based Android project
    https://github.com/uber/NullAway#android
    http://wittchen.io/2017/09/15/integrating-nullaway-with-an-android-project/
    54

    View Slide

  55. Keeping up good practices in greenfield projects
    ● Choose one approach or several approaches
    ● Apply chosen solutions to your work routine and CI server
    ● Perform code reviews carefully
    ● Be consistent
    55

    View Slide

  56. How about legacy code?
    ● Apply proper rules to the new code (see previous slide)
    ● Perform null-checks and write more unit tests for the old code
    ● Keep backward compatibility when necessary
    ● Gradually deprecate old code and introduce new rules
    56

    View Slide

  57. Are there languages without null?
    57

    View Slide

  58. Are there languages without null/nil/None/etc.?
    58

    View Slide

  59. Languages without null
    Languages with Option type:
    ● Rust
    ● Ocaml
    ● Standard ML
    ● F#
    ● Scala
    Languages with Maybe type:
    ● Elm
    ● Haskell
    and others I could miss...
    Crystal has nil, but compiler prevents NPEs at compile time
    59

    View Slide

  60. Summary
    ● Do not pass null
    ● Do not return null
    ● Return empty collections instead of null
    ● Use Optional/Option type for returning “nullable” sync data
    ● Use RxJava Maybe type for returning “nullable” async data
    ● Make data in your code as much immutable as possible
    ● Use compile time analysis and static code analysis
    ● Use language without null or with null-safety features when you can
    ● Be careful during code reviews
    ● Be consistent during applying good practices
    60

    View Slide

  61. Fixing a billion dollar mi$take
    Thank you for attention! Questions?
    Piotr Wittchen
    wittchen.io
    github.com/pwittchen
    61

    View Slide