Java构造函数风格:检查参数不为null

如果你有一个接受一些参数的类但是没有一个被允许为null那么最佳实践是什么?

以下是显而易见的,但是有一点不明确:

 public class SomeClass { public SomeClass(Object one, Object two) { if (one == null || two == null) { throw new IllegalArgumentException("Parameters can't be null"); } //... } } 

这里的exception让你知道哪个参数是空的,但是构造函数现在很丑陋:

 public class SomeClass { public SomeClass(Object one, Object two) { if (one == null) { throw new IllegalArgumentException("one can't be null"); } if (two == null) { throw new IllegalArgumentException("two can't be null"); } //... } 

这里的构造函数是整洁的,但现在的构造函数代码不是真的在构造函数中:

 public class SomeClass { public SomeClass(Object one, Object two) { setOne(one); setTwo(two); } public void setOne(Object one) { if (one == null) { throw new IllegalArgumentException("one can't be null"); } //... } public void setTwo(Object two) { if (two == null) { throw new IllegalArgumentException("two can't be null"); } //... } } 

哪种风格最好?

还是有一个更广泛接受的替代scheme?

第二或第三。

因为它告诉你的API的用户究竟发生了什么错误。

为了减less冗长,请使用commons-lang中的Validate.notNull(obj, message) 。 因此你的构造函数将如下所示:

 public SomeClass(Object one, Object two) { Validate.notNull(one, "one can't be null"); Validate.notNull(two, "two can't be null"); ... } 

将支票放入二传手也是可以接受的,同样的详细评论。 如果你的设置者也有保持对象一致性的angular色,你也可以select第三个。

您可以使用旨在促进先决条件检查的许多库中的一个。 Google Guava中的许多代码使用com.google.common.base.Preconditions

在自己的方法开始时调用简单的静态方法来validation正确的参数和状态。 这允许构造如

  if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); } 

用更紧凑的代替

  checkArgument(count > 0, "must be positive: %s", count); 

它具有在番石榴广泛使用的 checkNotNull 。 然后你可以写:

  import static com.google.common.base.Preconditions.checkNotNull; //... public SomeClass(Object one, Object two) { this.one = checkNotNull(one); this.two = checkNotNull(two, "two can't be null!"); //... } 

大多数方法被重载,不会收到错误消息,固定的错误消息,或带有可变参数的模板化错误消息。


IllegalArgumentException vs NullPointerException

当您的原始代码在null参数上抛出IllegalArgumentException时,Guava的Preconditions.checkNotNull会抛出NullPointerException

下面是Effective Java 2nd Edition的一句话:Item 60:赞成使用标准exception

有争议的是,所有错误的方法调用都归结为非法的论证或者非法的状态,但是其他的例外标准被用于某些非法的论证和状态。 如果调用者在某些禁止使用空值的参数中传递null值,则会抛出NullPointerException而不是IllegalArgumentException

NullPointerException不仅仅用于访问null引用的成员; 当参数为非法值时,抛出它们是非常标准的。

 System.out.println("some string".split(null)); // throws NullPointerException 

老问题; 另一个新的答案(已经提到另一个评论,但我认为值得自己的答案)。

Java 7将java.lang.Objects.requireNonNull()添加到每个人都可以使用的API中。 所以检查null的所有参数归结为一个简短的列表,如:

 this.arg1 = Objects.requireNonNull(arg1, "arg1 must not be null"); this.arg2 = Objects.requireNonNull(arg2, "arg2 must not be null"); 

边注:

  • 确保不要颠倒这两个参数 – 第二个是在第一个参数为空的情况下将被用于NPE的消息(如果您将它们反转,那么您的支票将永远不会失败)
  • 另一个最佳实践是:如果可能的话,让所有的类成员都是最终的(所以你可以肯定的是:当某个对象被创build成功时,它的所有成员都不为空;而且它们不会随时间而改变)

我会有一个实用的方法:

  public static <T> T checkNull(String message, T object) { if(object == null) { throw new NullPointerException(message); } return object; } 

我会让它返回的对象,以便您可以在这样的任务中使用它:

  public Constructor(Object param) { this.param = checkNull("Param not allowed to be null", param); } 

编辑:关于使用第三方库的build议,Google Preconditions特别是做了以上甚至比我的代码更好。 但是,如果这是将图书馆纳入项目的唯一原因,我会犹豫不决。 这个方法太简单了。

除了上面给出的答案都是有效的和合理的,我认为可以指出,检查空值不是必要的“良好实践”。 (假设除OP之外的读者可能会把这个问题视为教条主义)

从Misko Hevery博客的可测性: 断言或不断言

在Java中检查先决条件的方法比较 – Guava与Apache Commons与Spring Framework与Plain Java Asserts的比较

 public static void fooSpringFrameworkAssert(String name, int start, int end) { // Preconditions Assert.notNull(name, "Name must not be null"); Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")"); // Do something here ... } public static void fooApacheCommonsValidate(String name, int start, int end) { // Preconditions Validate.notNull(name, "Name must not be null"); Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end); // Do something here ... } public static void fooGuavaPreconditions(String name, int start, int end) { // Preconditions Preconditions.checkNotNull(name, "Name must not be null"); Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end); // Do something here ... } public static void fooPlainJavaAsserts(String name, int start, int end) { // Preconditions assert null != name : "Name must not be null"; assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")"; // Do something here ... } 

这是本文的总结: http : //www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-java

抛出一个未经检查的exception的替代方法是使用assert 。 否则,我会抛出检查exception,使调用者意识到这一事实,该构造函数将不会使用非法值。

你的头两个解决scheme之间的区别 – 你需要一个详细的错误信息,你需要知道哪个参数失败或足够了解,实例不能由于非法参数创build?

请注意,第二个和第三个示例无法正确报告两个参数都为空。

顺便说一句 – 我投票的变化(1):

 if (one == null || two == null) { throw new IllegalArgumentException( String.format("Parameters can't be null: one=%s, two=%s", one, two)); } 

对于静态分析的注释也是有用的,或者是在运行时间检查之外,也可以是in-place。

例如,FindBugs提供@NonNull注解。

public SomeClass(@NonNull Object one,@NonNull Object two){

你可以简单地有一个方法,它需要你需要validation的所有构造函数参数。 这个方法根据哪个参数是无效的抛出exception。 你的构造函数调用这个方法,如果它通过,它初始化值。

我假设你谈论Java中的内置assert 。 在我看来,使用它并不是一个好主意。 由于它可以使用命令行参数打开/closures。 所以有人说只能用私人方法才能接受。

我的导师告诉我不要重新发明轮子! 他们的build议是使用图书馆。 他们(可能)很好的devise和testing。 当然,你有责任确保你抓住一个高质量的图书馆。

其他人则告诉我说,Enterprise ppl – 在某些方面 – 是错误的,而且对于简单的任务,您会引入更多的依赖性 – 而不是必需的。 我也可以接受这一点…但这是我最近的经验:

首先,我写了我自己的私人方法来检查空参数。 这是无聊和多余的。 我知道我应该把它放进一个Utility类。 但是,为什么我要把它写在第一个地方呢? 我可以节省时间不写unit testing和devise一个现有的东西。 除非你想锻炼或学习,否则我不会推荐这样做。

我最近开始使用谷歌的番石榴,我发现,一起开始使用它们,你不会仅仅使用这种单一的方法。 你会发现和使用它越来越多。 最后,这将使您的代码更短,更可读,更一致,更易于维护。

顺便说一句:根据你的目标,我会去2或3使用上面提到的图书馆之一…