Avoiding nulls, immutability, object state
I like the concept of immutability. I also like the concept of no nulls (and if possible also no NullDesignPattern, no NullObjects... as much as possible).
But what about the following scenario:
I have an object User
, which has two fields: birthday
and dateInLifeMarried
(could be any similar field; the important thing is that at first this field is null
and changes at some point in object life).
As it's immutable I want both fields to be in the constructor:
public User(birthday, dateInLifeMarried)
Now:
null
to the second parameter null
Am i just contradicting myself or is there an elegant way to have it which i'm not thinking of?
Well you need to consider what you want the representation to be - not just the constructor signature. I suspect you will need to use a null reference (or at least something similar) for the field. But you could then have two constructors:
User(birthday) // Unmarried user
User(birthday, marriageDate) // Married user; marriageDate must not be null
As the user of an API, I'm not sure that I'd really like that though - I think I'd rather allow marriageDate
to be null. In particular, it would be annoying to have to write:
LocalDate marriageDate = getMarriageDateOrNull();
User user = marriageDate == null ? new User(birthday)
: new User(birthday, marriageDate);
Alternatively, as people can get married more than once, you could always take an Iterable<LocalDate> marriageDates
, then a never-married user would have an empty sequence :) (You'd need to consider married-then-divorced and married-then-widowed users though. Modelling real life is hard.)
What about a second constructor
User(birthday)
that calls the first
this(birthday, null)
?
Don't just avoid null for the sake of avoiding it. Think about what null
means and then use it where it makes sense. The value null
represents an unknown, undefined or unspecified value. If you use null consciously you can make your code easier to use and more readable, and at the same time prevent null pointer exceptions.
In your example the marriage date can be unspecified, and therefore you should allow null
. However, you don't want the birthday to be unspecified, so you don't allow null there. You could specify the constructors like this:
User(LocalDate birthday)
{
this(birthday, null);
// That's it! Nothing more to do here.
}
User(LocalDate birthday, LocalDate marriageDate)
{
if (birthday == null)
throw new IllegalArgumentException();
// Use it...
}
Now you (or anyone that uses your code) can do stuff like this:
LocalDate marriageDate = getMarriageDateOrNull();
User user = new User(birthday, marriageDate);
See? Cleaner code because you empower null
instead of avoiding it.
上一篇: 指针与Java和字符串变量
下一篇: 避免空值,不变性,对象状态