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.

E8bb35e5d8c868e7dffcd9859248d6f1?s=128

Piotr Wittchen

February 08, 2018
Tweet

Transcript

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

  2. What is a billion dollar mistake? 2

  3. null 3

  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

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

    null 5
  6. Passing null 6

  7. What could happen if we pass null to our method?

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

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

    == null) { throw new NullPointerException("data is null!"); } data.contains("foo"); } 9
  10. Objects.requireNonNull(...) method introduced in Java 7 public void processData(String data)

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

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

    12
  13. Returning null 13

  14. Dealing with collections public List<Integer> createList() { List<Integer> list =

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

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

    new ArrayList<>(); ... if(canReturnList) { return list; } return new ArrayList<>(); } 16
  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
  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
  19. Null Object Pattern NullableObject object; Object returnedValue; if(!object.isNull()) { returnedValue

    = object.getObject(); } 19
  20. Optional type in Guava final Optional<Object> optional = Optional.of(new Object());

    if (optional.isPresent()) { Object notNull = optional.get(); } 20
  21. Optional type in Guava final Optional<Object> optional = Optional.absent(); final

    boolean isPresent = optional.isPresent(); assertThat(isPresent).isFalse(); 21
  22. Optional type in Guava final Optional<Object> optional = Optional.fromNullable(null); final

    boolean isPresent = optional.isPresent(); assertThat(isPresent).isFalse(); https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained 22
  23. Optional type in Java 8 Optional<Object> optional = Optional.of(new Object());

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

    returnedValue = optional.orElse(StringUtils.EMPTY); assertThat(returnedValue).isInstanceOf(String.class); 24
  25. Optional type in Java 8 Optional<Object> optional = Optional.of(new Object());

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

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

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

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

    .stream() .flatMap(Optional::stream) .collect(Collectors.toList()); 29
  30. We have even better Option (in Vavr) Option<Object> option =

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

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

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

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

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

    = Option.of(new Object()); option .peek(object -> /* handle object here*/) .onEmpty(() -> /* object does not exist*/); 35
  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
  37. We have even better Option (in Vavr) Option<String> option =

    Option.of("my string"); String result = Match(option).of( Case($Some($()), String::toUpperCase), Case($None(), () -> StringUtils.EMPTY) ); assertThat(result).isEqualTo("MY STRING"); 37
  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
  39. Optional won’t save us from NPEs Optional<Object> optional = Optional.of(new

    Object()); ... 39 NullPointerException optional = null; optional.isPresent();
  40. Let’s talk about immutability Object object = new Object(); object

    = null; object.toString(); 40 NullPointerException!
  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
  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
  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
  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
  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
  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
  47. Maybe type in RxJava 47 Maybe is lazy and asynchronous

  48. Maybe type in RxJava Maybe<Object> maybe = createMaybe(); maybe .subscribeOn(Schedulers.io())

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

    .subscribe(new MaybeObserver<Object>() { @Override public void onSubscribe(Disposable d) { } @Override public void onSuccess(Object o) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); 49
  50. Compile time analysis 50

  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
  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
  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
  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
  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
  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
  57. Are there languages without null? 57

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

  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
  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
  61. Fixing a billion dollar mi$take Thank you for attention! Questions?

    Piotr Wittchen wittchen.io github.com/pwittchen 61