9

I'm trying to create a contact book application, and currently I'm working on a class called Person. One of the fields of person is dateOfBirth, however I don't know how to handle unknown dates. Until now I have been using java.time.LocalDate for dateOfBirth and I've been initialising the field to null at instantiation. This isn't ideal, because it's resulting in null pointer exceptions when I try to compare dateOfBirth to other dates such as the LocalDate.now().

What would be the best way to handle dateOfBirth? Should I just create an entirely new class and manage unknown dates internally? Or is ther some better way to do it. I was going to just extend LocalDate but it's immutable so can't be extended without going to the source.

Helios
  • 193
  • 1
  • 1
  • 3
  • 1
    This really may depend on your application. In some cases it may work or make sense to set an arbitrary date for unknown (say 31.12.1799) in others this may lead to difficult to track errors, like if you need to decide if somebody is of full age to make a purchase. – thorsten müller Jun 13 '15 at 09:18
  • 1
    I take it this isn't a question of how to handle a bad date night? :) – Alex Jun 13 '15 at 11:05

4 Answers4

9

Use the class Optional<LocalDate>

This useful container class was introduced in Java 8. It is a wrapper used for values which you might or might not know. It is unboxed with various methods which all allow you to handle the case that the value is unknown in a graceful manner.

  • get() unboxes the LocalDate, but throws a NoSuchElementException in case you have none. Unfortunately it is an unchecked exception, just like the dreaded NullPointerException. So I would recommend to avoid using this method and instead use:
  • orElseThrow(exception) which throws said exception when you don't have a value. The exception can (and should be) a checked exception so you are forced to handle it. Don't feel like writing a clunky try...catch... construct? In that case:
  • ifPresent(Consumer<? super T> consumer) is a useful construct when you want to execute some code only if you have a value and otherwise do nothing.
  • orElse(T value) unboxes the value but gets you the dummy value you provide when you have none

For more information, check the article "Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!" on Oracle Tech Net and the documentation to see what other methods it provides.

Not using Java 8 yet?

When you are still using an older version of Java, you could easily improvise your own OptionalDate class, which has just:

  • private LocalDate
  • public void set(LocalDate value) and
  • public LocalDate get() throws DateUnknownException

By having the getter throw a checked exception you are forced to always handle the null-case.

Philipp
  • 23,166
  • 6
  • 61
  • 67
  • 2
    Pre-java 8 you can use Guava Optional – Daenyth Jun 13 '15 at 17:50
  • 1
    If he is using `java.time.LocalDate` then he is using Java 8 already... – Marcelo Glasberg Jun 14 '15 at 07:06
  • 1
    @marcg I know, but I wanted the answer to be applicable for a wider audience. The problem doesn't just apply to this one class, it applies to every situation where you have variables which can have an "unknown" state. – Philipp Jun 14 '15 at 09:31
  • 1
    You should probably not use `Optional` as a field - it's really meant to be used as a return type from a method. So in this case I would have a `private LocalDate dateOfBirth` field that may be null - but `getAge` or `getDateOfBirth` could return an `OptionalInt` or `Optional` respectively. – assylias Jun 14 '15 at 18:02
1

Tony Hoare is said to have invented, what we now call null. And he regretted it:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years

Source: wikipedia.

Now we have to live and deal with it.

however I don't know how to handle unknown dates.

Trust me, you are not alone.

The problem is, that there is something I would call ambiguity of information. Asking »when was the battle of waterloo?« in everyday life »I don't know« is an accepted answer. But it is not the answer about the date itself, it is about the knowledge of the speaker.

To your problem: Asking person.getBirthDate() gives you an ambigous answer:

  • either it was on 1952-11-02

  • or the equivalent to I don't know, which is null.

What is a proper solution to that misery of ambiguity?

Since, we have null and since it is widely accepted across datastores, you should use null as a correct answer in the context of the DB.

But what about your programm:

As you correctly said

because it's resulting in null pointer exceptions when I try to compare dateOfBirth to other dates such as the LocalDate.now()

You have to guard against that in some way or the other:

1) You could introduce a getter person.birthdayKnown() to do the following:

if (personA.birthdayKnown() && personA.birthdayKnown()) // allow further operations

2) With the advent of Java8, you could make it an Optional and work with isPresent().

Thomas Junk
  • 9,405
  • 2
  • 22
  • 45
1

Your options are:

1) Use null. Then you must not forget to check for null. Annotations @NotNull and @Nullable (and similar) will help you avoid NullPointerException's.

2) Use Optional. This is like being forced to check for null. But it adds some overhead and other indirections.

3) Use the DefaultObject pattern: Create a static final LocalDate DEFAULT as the default. I don't like this, since later it's easy to forget you don't have a real, known date.

4) Use the NullObject pattern: Create your own class that will wrap a java.time.LocalDate, and then, instead of null, define and consistently use: static final MyLocalDate UNKNOWN

5) Use an utility class to always compare or process dates. So, instead of date1.foo(date2) you would use UtilDates.foo(date1, date2). Method foo should accept nulls.

But before choosing one of these options you must decide what to do when you don't know the date. For example, when checking if some person is older than 21, but dateOfBirth is unknown, what do you wanna do? Do you want to always assume the person is older than 21? Or always younger than 21? Do you want to issue a warning and abort the current operation, forcing the user to enter a date? Depending on what you want to do each of the options above have pros and cons.

There is another one, maybe not an option in your case, but:

6) Force the user to choose a valid date (i.e., disallow nulls) when Person is being instantiated/saved.

0

As @Philipp said, Optional is a good answer to this problem. However, it requires Java 8 (even if you can implement it yourself), and this kind of structure is typically adapted for functional programming style, and could therefore seem strange in a object oriented system.

A typical OOP alternative is Null Object. See for instance my answer on this other post. It allows you to define a "default behaviour" when the object is not set, which is what you want.

mgoeminne
  • 1,158
  • 6
  • 11