pres_lightning_nullref



pres_lightning_nullref

0 0


pres_lightning_nullref

Lightningtalk: Null Reference The Billion Dollar Mistake

On Github bjartek / pres_lightning_nullref

Null Reference

The Billion (american) Dollar Mistake

Bjarte Stien Karlsen

slides created with revealit/reveal.js

Agenda

  • why is null reference a problem?
  • streetname example
  • the java way manual style
  • the grooooooovy way
  • guava optional style
  • scala style

Why is null reference a problem?

NullPointerException

Checking for NULL is not enforced by the java compiler.

Cognitive burden

  • the programmer has to ensure that the code check for Null

..tools might help us?

  • Annotations like @NotNull and @Nullable might help
  • But there are a ton of variants and IMHO none are without problems.
  • Might help, but does not solve the problem. You can choose to ignore warnings/ide help/checkstyle errors

Task

Return the streetname of the address where a person lives

Person POJO

package org.bjartek.nullref.model;

public class Person {

    private final Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }
}

Address POJO

package org.bjartek.nullref.model;

public class Address {

    private final String streetname;

    public Address(String streetname) {
        this.streetname = streetname;
    }

    public String getStreetName() {
        return streetname;
    }
}

The Java Way manual Style

Java way

package org.bjartek.nullref;


import com.google.common.base.Objects;
import org.bjartek.nullref.model.Person;

public class PlainJava implements StreetnameProvider {

    @Override
    public String streetName(Person person) {
        String name = person.getAddress().getStreetName();
        return Objects.firstNonNull(name, StreetnameProvider.DEFAULT);
    }
}

Java way take 2

package org.bjartek.nullref;

import com.google.common.base.Objects;
import org.bjartek.nullref.model.Person;

public class PlainJavaNullsafe implements StreetnameProvider {

    @Override
    public String streetName(Person person) {
        if (person == null || person.getAddress() == null) {
            return StreetnameProvider.DEFAULT;
        }
        String name = person.getAddress().getStreetName();
        return Objects.firstNonNull(name, StreetnameProvider.DEFAULT);
    }
}

The Grooooovy Way

?. POWAH

package org.bjartek.nullref

import com.google.common.base.Objects import org.bjartek.nullref.model.Person

class Groovy implements StreetnameProvider {

@Override
String streetName(Person person) {
    String name = person?.address?.streetName
    return Objects.firstNonNull(name, StreetnameProvider.DEFAULT)
}

}

Guava Optional

Optional

package org.bjartek.nullref;

import com.google.common.base.Optional;
import org.bjartek.nullref.gmodel.Person;

public class GuavaManual implements StreetnameProviderOptional {

    @Override
    public String streetName(Optional<Person> p) {
        if (!p.isPresent()) {
            return StreetnameProviderOptional.DEFAULT;
        }
        Person person = p.get();
        if (!person.getAddress().isPresent()) {
            return StreetnameProviderOptional.DEFAULT;
        }

        Optional<String> name =    person.getAddress().get().getStreetName();
        return name.or(StreetnameProviderOptional.DEFAULT);
    }
}

  • javadoc for Optional in guava svn

YUCK!

Optional another way?

package org.bjartek.nullref;

import com.google.common.base.Optional;
import org.bjartek.nullref.gmodel.*;

public class GuavaIterator implements StreetnameProviderOptional {

    @Override
    public String streetName(Optional<Person> p) {
        for (Person person : p.asSet()) {
            for (Address adr : person.getAddress().asSet()) {
                return adr.getStreetName().or(StreetnameProvider.DEFAULT);
            }
        }
        return StreetnameProviderOptional.DEFAULT;
    }
}

Scala style

map/flatmap

case class Address(streetName: Option[String])
case class Person(address: Option[Address])

def streetName(person:Option[Person]): String = {
  val name = person.flatMap{pers =>
    pers.address.flatMap{adr =>
      adr.streetName
    }
  }
  name.getOrElse("Not Found")
}

for comprehension

case class Address(streetName: Option[String])
case class Person(address: Option[Address])

def streetName(person:Option[Person]): String = {
  val name = for { 
    p <- person
    a <- p.address
    sn <- a.streetName
  } yield sn

  name.getOrElse("Not Found")
}

What did we just use?

MONAD!

Conclusion

  • Tools that help the compiler find NPE is a good thing
  • Tools such as Google Guava's Optional are very verbose
  • NPE's are almost non existing in idiomatic scala due to Option monad.