我喜欢不变性的概念。我也喜欢no nulls的概念(如果可能的话,也没有NullDesignPattern,尽可能没有NullObjects。)
但是下面的场景呢?
我有一个对象User
,它有两个字段:birthday
和dateInLifeMarried
(可以是任何类似的字段;重要的是,这个字段最初是{{1}并且在对象生命中的某些点上发生了变化。)
由于它是不可变的,我希望两个字段都在构造函数中:
null
现在:
public User(birthday, dateInLifeMarried)
传递给第二个参数null
我只是自相矛盾,还是有 优雅的方式 来拥有它,我不会想到它?
答案 0 :(得分:13)
你需要考虑你想要的表示 - 而不仅仅是构造函数签名。我怀疑你将需要为该字段使用空引用(或至少类似的东西)。但是你可以有两个构造函数:
User(birthday) // Unmarried user
User(birthday, marriageDate) // Married user; marriageDate must not be null
作为API的用户,我不确定我是否真的喜欢它 - 我想我宁愿允许marriageDate
为空。特别是,必须写下来会很烦人:
LocalDate marriageDate = getMarriageDateOrNull();
User user = marriageDate == null ? new User(birthday)
: new User(birthday, marriageDate);
或者,由于人们可以不止一次结婚,你总是可以选择Iterable<LocalDate> marriageDates
,然后一个从未结婚的用户会有一个空序列:)
(你需要考虑已婚后离婚和已婚丧偶的用户。模拟现实生活很难。)
答案 1 :(得分:1)
第二个构造函数
怎么样?User(birthday)
调用第一个
this(birthday, null)
答案 2 :(得分:1)
不要为了避免它而避免使用null。考虑null
的含义,然后在有意义的地方使用它。值null
表示未知,未定义或未指定值。如果你有意识地使用null,你可以使你的代码更容易使用和更易读,同时防止空指针异常。
在您的示例中,可以不指定结婚日期,因此您应该允许null
。但是,您不希望未指定生日,因此您不允许使用null。您可以像这样指定构造函数:
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...
}
现在你(或任何使用你代码的人)可以做这样的事情:
LocalDate marriageDate = getMarriageDateOrNull();
User user = new User(birthday, marriageDate);
请参阅?更清洁的代码,因为您授权 null
而不是避免它。
答案 3 :(得分:0)
您可以使用继承:
public abstract class User{}
public class UnMarriedUser extends User{}
public class MarriedUser extends User{}
但是,面对许多排列,这种模式变得越来越弱。