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

发布于 2024-09-04 22:30:26 字数 1360 浏览 3 评论 0原文

如果您有一个类接受某些参数但其中任何参数都不允许为 null,那么最佳实践是什么?

下面的内容很明显,但异常有点不具体:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        if (one == null || two == null)
        {
            throw new IllegalArgumentException("Parameters can't be 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");
        }
        //...
  }

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

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");
        }
        //...
     }
  }

这些风格中哪一个最好?

还是有更广泛接受的替代方案?

What are the best practices if you have a class which accepts some parameters but none of them are allowed to be null?

The following is obvious but the exception is a little unspecific:

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

Here the exceptions let you know which parameter is null, but the constructor is now pretty ugly:

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");
        }
        //...
  }

Here the constructor is neater, but now the constructor code isn't really in the constructor:

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");
        }
        //...
     }
  }

Which of these styles is best?

Or is there an alternative which is more widely accepted?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

白芷 2024-09-11 22:30:26

第二个或第三个。

因为它告诉 API 的用户到底出了什么问题。

为了减少冗长,请使用 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");
    ...
}

将检查放在 setter 中也是可以接受的,具有相同的详细注释。如果您的 setter 还具有保持对象一致性的作用,您也可以选择第三个。

The second or the third.

Because it tells the user of your API what exactly went wrong.

For less verbosity use Validate.notNull(obj, message) from commons-lang. Thus your constructor will look like:

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

Placing the check in the setter is also acceptable, with the same verbosity comment. If your setters also have the role of preserving object consistency, you can choose the third as well.

荒芜了季节 2024-09-11 22:30:26

Java 7 将 java.util.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 的消息,如果第一个参数为空(如果你反转它们,那么你的检查将永远不会失败)
  • 另一个最佳实践:如果可能,使所有类成员为最终的(这样你就可以确定:当某个对象已成功创建,其所有成员均不为空;并且它们不会随时间变化)

Java 7 added java.util.Objects.requireNonNull() to the APIs everybody can use. So checking all arguments for null boils down to a short list like:

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

Side notes:

  • make sure to not reverse the two arguments - the second one is the message that will be used for the NPE which is thrown if the first argument is null (if you reverse them, well, then your check will never fail)
  • another best practice: if possible, make all your class members final (so you can be sure: when some object has been created successfully, all its members are not null; and they won't change over time)
作死小能手 2024-09-11 22:30:26

您可以使用旨在促进前提条件检查的众多库之一。 Google Guava 中的许多代码都使用 com.google.common.base.Preconditions

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

之类的构造

 if (count <= 0) {
   throw new IllegalArgumentException("必须为正数:" + count);
 }

更换为更紧凑的

 checkArgument(count > 0, "必须为正数:%s", count);

结构它有 checkNotNull在 Guava 中广泛使用。然后你可以这样写:

 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!");
     //...
 }

大多数方法都被重载,要么不接受错误消息,要么接受固定的错误消息,要么接受带有可变参数的模板化错误消息。


On IllegalArgumentExceptionNullPointerException

虽然原始代码在 null 参数上抛出 IllegalArgumentException,但 Guava 的 Preconditions.checkNotNull< /code> 会抛出 NullPointerException

以下是《Effective Java 第二版:第 60 条:支持使用标准异常》的引文:

可以说,所有错误的方法调用都可以归结为非法参数或非法状态,但其他异常通常用于某些非法参数和状态。如果调用者在某些禁止使用 null 值的参数中传递 null,则约定将抛出 NullPointerException 而不是 IllegalArgumentException

NullPointerException 并不是仅在访问 null 引用的成员时才保留;而是在访问 null 引用的成员时才保留。当参数为非法值时抛出它们是非常标准的。

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

You can use one of the many libraries designed to facilitate precondition checks. Many code in Google Guava uses com.google.common.base.Preconditions

Simple static methods to be called at the start of your own methods to verify correct arguments and state. This allows constructs such as

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

to be replaced with the more compact

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

It has checkNotNull that is used extensively within Guava. You can then write:

 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!");
     //...
 }

Most methods are overloaded to either take no error message, a fixed error message, or a templatized error message with varargs.


On IllegalArgumentException vs NullPointerException

While your original code throws IllegalArgumentException on null arguments, Guava's Preconditions.checkNotNull throws NullPointerException instead.

Here's a quote from Effective Java 2nd Edition: Item 60: Favor the use of standard exceptions:

Arguably, all erroneous method invocations boil down to an illegal argument or an illegal state, but other exceptions are standardly used for certain kinds of illegal argument and states. If a caller passes null in some parameter for which null values are prohibited, convention dictates NullPointerException be thrown rather than IllegalArgumentException.

A NullPointerException isn't reserved for just when you access members of a null reference; it's pretty standard to throw them when an argument is null when that's an illegal value.

System.out.println("some string".split(null));
// throws NullPointerException
心清如水 2024-09-11 22:30:26

我会有一个实用方法:

 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);
 }

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

I would have a utility method:

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

I would have it return the object so that you can use it in assignments like this:

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

EDIT: Regarding the suggestions to use a third party library, the Google Preconditions in particular does the above even better than my code. However, if this is the only reasons to include the library in your project, I'd be hesitant. The method is too simple.

怕倦 2024-09-11 22:30:26

除了上面给出的所有有效且合理的答案之外,我认为最好指出也许检查 null 并不是必要的“良好实践”。 (假设除了 OP 之外的读者可能会认为这个问题是教条的)

来自 Misko Hevery 关于可测试性的博客:
断言或不断言

Apart from the answers given above which are all valid and reasonable, I think it's good to point out that maybe checking for null isn't necessary "good practice". (Assuming readers other than the OP might take the question as dogmatic)

From Misko Hevery blog on testability:
To Assert or Not To Assert

人│生佛魔见 2024-09-11 22:30:26

抛出未经检查的异常的替代方法是使用 assert。否则,我会抛出已检查的异常,以使调用者意识到构造函数无法使用非法值。

前两个解决方案之间的区别 - 您是否需要详细的错误消息,您是否需要知道哪个参数失败,或者是否足以知道由于非法参数而无法创建实例?

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

顺便说一句 - 我投票支持 (1) 的变体:

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

An alternative to throwing an unchecked exception would be the usage of assert. Otherwise I´d throw checked exceptions to make the caller aware of the fact, that the constructor will not work with illegal values.

The difference between your first two solutions - do you need a detailed error message, do you need to know which parameter failed or is it enough to know, that the instance couldn't have been created due to illegal arguments?

Note, that the second and third example can't report correctly that both parameters have been null.

BTW - I vote for a variation of (1):

if (one == null || two == null) {
    throw new IllegalArgumentException(
      String.format("Parameters can't be null: one=%s, two=%s", one, two));
}
乱了心跳 2024-09-11 22:30:26

Java 中检查前提条件的方法比较 - Guava、Apache Commons、Spring Framework、Plain Java 断言,

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

Comparison of Ways to Check Preconditions in Java - Guava vs. Apache Commons vs. Spring Framework vs. 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 ...
    }

this is summary of this article:
http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-java

如果没有 2024-09-11 22:30:26

静态分析的注释也很有用,无论是作为运行时检查的补充还是代替运行时检查。

例如,FindBugs 提供了 @NonNull 注释。

public SomeClass(@NonNull 对象一,@NonNull 对象二) {

Annotations for static analysis are also useful, either in-addition-to or in-place-of the run-time checks.

FindBugs, for example, provides an @NonNull annotation.

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

兔小萌 2024-09-11 22:30:26

您可以简单地使用一个方法来获取需要验证的所有构造函数参数。此方法会引发异常并显示特定消息,具体取决于哪个参数无效。
您的构造函数调用此方法,如果它通过,它将初始化值。

You can simply have a method which takes all the constructor arguments that you need to validate. This method throws exception with specific message depending on which argument is not valid.
Your constructor calls this method, and if it passes, it initialize values.

话少心凉 2024-09-11 22:30:26

我假设您谈论的是 Java 中内置的 assert。在我看来,使用它并不是一个好主意。因为它可以使用命令行参数打开/关闭。 因此有人说它只能在私有方法中使用。

我的导师告诉我不要重新发明轮子!他们的建议是使用图书馆。它们(可能)经过精心设计和测试。当然,您有责任确保您拥有一个高质量的库。

其他人告诉我,Enterprise ppl - 在某些方面 - 是错误的,对于简单的任务,您引入了比所需更多的依赖性。我也可以接受这一点......但这是我的最新经验:

首先我编写了自己的私有方法来检查空参数。这很无聊而且多余。我知道我应该将它放入实用程序类中。但是既然有人已经这样做了,我为什么要首先写它呢?我可以节省时间而不编写单元测试并设计现有的东西。除非你想锻炼或学习,否则我不建议这样做。

我最近开始使用 google 的 guava,我发现 - 以及 apache commons - 一旦你开始使用它们,你就不会只使用单一方法。您会越来越多地发现并使用它。最后,这将使您的代码更短、更易读、更一致且更易于维护。

顺便说一句:根据您的目标,我会使用上面提到的库之一选择 2 或 3...

I assume that you talk about the built in assert in Java. In my opinion it's not a really good idea to use it. Since it can be turned on/off using command line parameters. Therefore some says it is only acceptable to use in private methods.

My mentors are telling me not to re-invent the wheel! Their advice is to use libraries. They are (probably) well designed and tested. Of course it is your responsibility to make sure you grab a good-quality library.

Others are telling me that Enterprise ppl - in some terms - are wrong and you introduce more dependency - for simple tasks - than required. I can accept that point too... But here is my latest experience:

First I wrote my own private method to check null parameters. It's boring and redundant. I know I should put it into a Utility class. But why should I write it at the first place, when someone has already has done it? I can save time not writing unit test and design an existing stuff. Unless you want to exercise or learn I wouldn't recommend to do so.

I recently started to use google's guava and I find that - along with apache commons - once you start to use them, you won't use just for that one single method. You'll discover and use it more and more. At the end, this'll make your code shorter, more readable, more consistent and more maintainable.

BTW.: Depending on your aims I would go with 2 or 3 using one of the mentioned library above...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文