使用assert或IllegalArgumentException作为必需的方法参数会更好吗?


87

在Java中,强烈建议使用哪个,为什么?两种类型都会抛出异常,因此在这方面处理它们是相同的。assert稍短一些,但我不确定有多重要。

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}

我更喜欢一个简单的obj.hashCode();-)
Marco

Answers:


117

谨防!

断言在运行时被删除,除非你明确指定为“启用断言”编译代码时。Java断言不应在生产代码上使用,而应限于私有方法(请参见Exception与Assertion),因为私有方法应被开发人员所知并使用。也assert将抛出AssertionError的延伸ErrorException和通常表明你有一个非常不正常的错误(如“OutOfMemoryError异常”,这是很难恢复过来,是不是?)不期望你能够为治疗。

删除“ enable assertions”标志,并与调试器进行检查,您将看到您不会踩IllegalArgumentException throw调用...,因为此代码尚未编译(同样,删除“ ea”时)

最好将第二种构造用于公共/受保护的方法,并且如果您希望在一行代码中完成某件事,那么我至少知道一种方法。我个人使用Spring FrameworkAssert类,该类具有一些用于检查参数的方法,并在失败时抛出“ IllegalArgumentException”。基本上,您要做的是:

Assert.notNull(obj, "object was null");

...实际上将执行与您在第二个示例中编写的代码完全相同的代码。还有其他一些有用的方法,例如hasTexthasLength在那里。

我不喜欢编写多余的代码,所以当我将编写的行数减少2(2行> 1行)时,我感到很高兴:-)


啊,我忘记断言了!好答案。我会稍等一下,看看是否还有其他东西,然后接受:)
Daenyth 2012年

2
请注意,没有标志可以在编译时删除断言(尽管可以通过条件编译将其删除)。断言默认是在运行时禁用的(我认为JVM将其视为NOOP),但可以通过java -ea和以编程方式启用。@Jalayn我认为在生产代码中包含断言是完全有效的,因为它们对于在现场
Justin Muller 2014年

@ Jalayn,-1。编译器并没有删除断言代码。除非您执行cmd,否则它们将不会运行java -ea
Pacerier 2014年

5
可以使用时无需使用Spring框架Objects.requreNonNull
雄辩的

46

您需要使用一个例外。使用断言将是对该功能的滥用。

未检查的异常旨在检测库用户的编程错误,而断言旨在检测您自己的逻辑中的错误。这些是单独的问题,不应混在一起。

例如,一个断言

assert myConnection.isConnected();

意思是“我知道导致该断言的每个代码路径都会确保myConnection已连接;如果上述代码未能获得有效的连接,则应该在到达此点之前抛出异常或返回”。

另一方面,支票

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

表示“在未建立连接的情况下调用我的库是编程错误”。


1
这些信息确实有帮助,但是我接受Jalayn的建议,因为它指出了我可能如何使用assert方法引入错误。
丹妮丝2012年

4
优点:“未经检查的异常旨在检测您的库用户的编程错误,而断言旨在检测您自己的逻辑中的错误。这些是单独的问题,不应混在一起。”
Asim Ghaffar 2013年

是的,但这是假设OP正在编写一个库。如果它们的代码仅供内部使用,则可以使用断言。
user949300 '18

11

如果编写的函数不允许null使用有效的参数值,则应将@Nonnull注释添加到签名中,并用于Objects.requireNonNull检查参数是否为,null并抛出NullPointerException是否为。该@Nonnull注释是对文档和将在某些情况下,编译时提供有用的警告。它不会阻止null在运行时传递。

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

更新:@Nonnull注释是不是Java标准库的一部分。相反,有来自第三方库的众多竞争标准(请参阅我应该使用哪个@NotNull Java注释?)。这并不意味着使用它不是一个坏主意,只是它不是标准的。


感谢您指出Objects.requireNonNull(),这对我来说是新的。您知道哪里有类似的“ requireTrue()”或“ requireEqual()”方法吗?没有什么反对Spring的断言,但并不是每个人都在使用它。
user949300

@ user949300 Objects.requireNonNull()用于参数验证。如果参数必须true等于或等于某个值,则该参数是没有意义的。对于非法参数以外的其他错误情况,您应该throw使用Exception更准确地描述错误的。也有JUnit,Assert但这是用于测试的。
雄辩的

我在想,更像是说平方根函数Objects.requireTrue(x >= 0.0);或一些散列Objects.requireEquals(hash.length == 256)
user949300

我必须使用哪个@Nonnull?javax.validation.constraints.NotNull吗?
Aguid

我将使用Eclipse JDT批注,因为它们是由聪明人制作的:)。文档:help.eclipse.org/neon/…-您也可以配置IntelliJ来使用它们。
koppor

2

我总是喜欢将IllegalArgumentException抛出断言。

断言通常用于JUnit或其他测试工具中,以检查/断言测试结果。因此,它可能给其他开发人员以错误的印象,即您的方法是一种测试方法。

方法传递了非法或不合适的参数时,抛出IllegalArgumentException也很有意义。这与Java开发人员遵循的“异常处理”约定更加一致。


5
断言比JUnit早40年左右-向C程序员询问ASSERT宏。
JBRWilkinson

3
这个问题不是关于c的。关于Java。所以我在Java的上下文中回答。
rai.skumar

不要混淆assert(保留关键字)和Assert(JUnit类),它们都用于对变量执行检查,但除此之外,它们是两个截然不同的事物,并且行为也截然不同。
Newtopian

1

IMO第二个稍好一点,因为它带来了更多的信息,并且可以进一步扩展(例如,通过扩展异常类)以提供更多信息,并且它不使用易于理解的否定比较。


1

我没有使用很多aserts,但是使用Lombock @NonNull的常用方法:https ://projectlombok.org/features/NonNull

Lombok实现:import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Java版本:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

龙目岛真的是一个非常不错的图书馆,我到处都在使用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.