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使用上面提到的图书馆之一…