Java构造函数样式:检查参数不为null

69 java null constructor coding-style

如果你有一个类接受一些参数但是不允许这些参数,那么最佳实践是null什么?

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

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

这里的异常让你知道哪个参数为null,但构造函数现在非常难看:

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");
        }
        //...
  }
Run Code Online (Sandbox Code Playgroud)

这里构造函数更整洁,但现在构造函数代码实际上不在构造函数中:

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");
        }
        //...
     }
  }
Run Code Online (Sandbox Code Playgroud)

哪种款式最好?

或者是否有更广泛接受的替代方案?

Boz*_*zho 91

第二或第三.

因为它告诉用户你的API究竟出了什么问题.

对于较少的冗长使用Validate.notNull(obj, message)来自commons-lang.因此,您的构造函数将如下所示:

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

将支票放入设定者也是可以接受的,具有相同的详细程度评论.如果您的setter还具有保持对象一致性的角色,您也可以选择第三个.

  • 在OP的示例中,setter不是final,这将允许子类违反约束.与构造函数的所有调用一样,这些方法应该是final或private. (9认同)
  • 引用公共语言的奖励积分 (7认同)
  • 为什么把它置于有争议的制定者?我认为情况正好相反.如果构造函数检查并阻止"null"值,那么如果setter接受它,我会觉得它是一个错误. (7认同)
  • 讨厌二传手.全部用于不可变对象. (5认同)
  • 现在,您可以使用Guava令人惊叹的库来实现这一目标.查找"checkArgument()".检查此链接以进行两者之间的比较:http://piotrjagielski.com/blog/google-guava-vs-apache-commons-for-argument-validation/(编辑:刚刚实现了guava已在讨论中提及,但没有链接提供了一个很好的比较) (2认同)

pol*_*nts 38

您可以使用众多库中的一个来促进前置条件检查.Google Guava中的许多代码都使用com.google.common.base.Preconditions

在您自己的方法开始时调用的简单静态方法,以验证正确的参数和状态.这允许诸如的构造

 if (count <= 0) {
   throw new IllegalArgumentException("must be positive: " + count);
 }
Run Code Online (Sandbox Code Playgroud)

被更紧凑的替换

 checkArgument(count > 0, "must be positive: %s", count);
Run Code Online (Sandbox Code Playgroud)

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!");
     //...
 }
Run Code Online (Sandbox Code Playgroud)

大多数方法都被重载为不带错误消息,固定错误消息或带有varargs的模板化错误消息.


IllegalArgumentExceptionvsNullPointerException

当你的原始代码抛出IllegalArgumentExceptionnull论点,番石榴的Preconditions.checkNotNull抛出NullPointerException来代替.

以下是Effective Java 2nd Edition的引用:第60项:支持使用标准异常:

可以说,所有错误的方法调用归结为非法论证或非法国家,但其他例外标准地用于某些类型的非法论证和状态.如果调用者传入null一些禁止空值的参数,则会NullPointerException抛出约定而不是IllegalArgumentException.

NullPointerException当您访问null引用的成员时,不保留A ; 当参数是null非法值时抛出它们是非常标准的.

System.out.println("some string".split(null));
// throws NullPointerException
Run Code Online (Sandbox Code Playgroud)

  • @hudolejev有趣的是,[`Object.requireNonNull`](http://docs.oracle.com/javase/7/docs/api/java/util/Objects.html#requireNonNull(java.lang.Object,java.lang.在JDK 7中引入的字符串))也会抛出一个`NullPointerException`,所以看起来圣战真的完成了. (6认同)
  • @hudolejev:我认为没有明确说明恰恰相反.IAE没有提到`null`,NPE说应用程序可以将它用于"null"的其他非法用途. (5认同)

Gho*_*ica 38

老问题; 另一个新的答案(另一个评论已经提到过;但我觉得值得自己回答).

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");
Run Code Online (Sandbox Code Playgroud)

附注:

  • 确保不反转这两个参数 - 第二个是将用于NPE的消息,如果第一个参数为null ,则抛出该消息(如果你反转它们,那么,你的检查永远不会失败)
  • 另一个最佳实践:如果可能的话,让所有的类成员都成为最终的(所以你可以肯定:当一个对象被成功创建时,它的所有成员都不为空;并且它们不会随着时间的推移而改变)

  • 这应该是现在接受的答案.没有第三个必要的lib +更新的JDK .... (5认同)

Yis*_*hai 5

我会有一个实用方法:

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

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

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

编辑:关于使用第三方库的建议,特别是 Google Preconditions 比我的代码做得更好。但是,如果这是将库包含在您的项目中的唯一原因,我会犹豫不决。方法太简单了。

  • 实际上它是实现了 Objects.requireNonNull(T obj) 或 Objects.requireNonNull(T obj, String message)。第二个抛出空指针异常。 (3认同)
  • 我相信 `Objects.notNull` 是为 JDK7 提出的。 (2认同)