23

为什么我们不能对公共方法使用断言?

我在某处读过

“断言在公共方法中是不合适的,因为该方法保证它将始终强制执行参数检查。公共方法必须检查其参数是否启用断言。此外,断言构造不会引发指定类型的异常。它只能抛出一个AssertionError“。

那么,它是否也适用于私有方法?
我没有清楚地理解上面的陈述

4

11 回答 11

27

重要的区别是您是否认为不正确的值是

a) 应该在代码中修复的编程错误。

b) 无法在代码中防止而需要在运行时处理的输入错误。

对于第一种情况,您应该使用断言,因为需要修复程序代码。如果是后一种情况,您应该使用适当的运行时或检查异常。


恕我直言,断言用于检测编程错误,而不是用户/外部输入。当您将拥有未被外部输入调用的公共方法时,也许作者将公共方法混淆为外部输入。

我会使用断言来检查参数以检测编程错误。恕我直言,这通常是他们的最佳用途。相比之下,私有方法只能由同一类中的代码调用,并且您应该期望它们是良好的单元测试并且可能的访问/使用有限。

我发现你更有可能通过公共接口出现编程错误,因为不同的人做出不同的假设(断言是记录和检查假设的好方法)内部检查没有你期望同一个程序员可以访问的有用如果没有编写整个内部代码库,则为内部代码。

于 2011-03-23T11:53:27.590 回答
16

断言不应该用于检查公共方法中的参数,原因如下:

  • 断言可以被禁用并且参数检查永远不应该被禁用,因为它们是方法与其调用者的合同的一部分
  • 断言失败不会为无效参数抛出适当的异常。

例子:

    /**
     * @throws ArithmeticException if divisor is zero
     */ 
    public void int divide(int divisor) {
        if (divisor == 0) {
            throw new ArithmeticException("Cannot divide by zero");
        }
        ...
    }

如果你在这里使用了一个断言,它可能会被关闭,它会抛出一个AssertionFailedException,这是无用的和无信息的。

于 2011-03-23T14:41:15.013 回答
3

就目前而言,我相信你引用的那句话是胡说八道。

可以肯定的是,assert 不是用于验证参数。

但是在每个重要的程序中都有(或应该有)许多不变量,这就是断言可能派上用场的地方。如果您可以在断言中表达不变量,请这样做,无论该方法是否是公共的。

然后,将发生以下情况之一:

a) 一切都很好。
b) 在运行时,程序因未完成的断言而失败。如果断言是正确的,那么不变量就被违反了,您就有机会找出原因并修复错误(或重新考虑您的设计)。

于 2011-03-23T12:20:36.023 回答
2

这可能是原始来源,来自 Java SE 指南“使用断言编程”。

不要使用断言来检查公共方法的参数。断言是不合适的,因为该方法保证它将始终强制执行参数检查。它必须检查其参数是否启用断言。此外,assert 构造不会引发指定类型的异常。它只能抛出一个 AssertionError。

这并不禁止公共方法中的断言。它禁止它们仅用于检查公共方法参数。

断言测试不变量。类控制发送到其私有方法的实际参数,并且可以保证不变量。一个类不控制发送给它的公共方法的实际参数,如果违反了先决条件,即使断言被关闭,也应该抛出异常。

有关何时使用断言的更多详细信息,请参见此处

于 2011-05-17T22:27:02.760 回答
1

一般来说,它似乎是健全的。尽管在某些情况下这可能有用。

考虑一下我们可能想要对database update我们知道存在的元素执行操作。然后查看例程是否成功可能很有用,例如:

public void update(Object o) {
   int nUpdatedObjects = dao.update(o);
   assert(nUpdatedObjects == 1)
}

在这种情况下,它使用故障快速原则validate服务于dao层。

于 2011-03-23T11:52:16.520 回答
1

断言用于调试;公共方法通常不应该通过调试断言来验证事物,而是通过进行适当的参数检查并抛出适当的异常。如果要验证内部对象状态,可以使用它,但不能验证参数。

于 2011-03-23T11:52:48.677 回答
1

我给出的回答并不完全正确。当然,您可以在公共方法(或任何您喜欢的地方)中使用断言。

重点更多的是你应该做什么。我自己完全理解其他人关于何时应该或不应该使用断言的反应。

但我必须承认我从不使用断言,而且我很少在代码中看到断言。我只工作了几年,但在我工作的 4 家完全不同的公司中,代码中没有任何断言。无论是用于预订航班的超过 1000 万行代码的 Web 应用程序、航天器控制中心、管理大型超市的软件还是错误跟踪器。他们都没有使用断言。所有公司都有不同的需求和方法。没有使用断言。

对我来说原因很简单。这里的人说断言是为了调试。没关系。并且您可以停用它们以提高速度。那也很好……一开始。程序越复杂,调试的时间就越多。还有一些错误,即使有 100% 的代码覆盖率,即使有广泛的集成和验证测试,您也只能在生产中发现它们。仅仅是因为您的用户最终比您更多地使用您的应用程序。他们不会像你一样使用它。

这很有趣,因为在生产日志中,我们不断从这样的代码中看到堆栈跟踪:

catch (MyException e) {
  logger.war("This should never happen",e);
}

这意味着您永远不知道生产中会发生什么。

如果你有机会进行检查,那就去做吧。当然日志注释在这里比有用更有趣,最好让异常弹出。

在所有情况下,不要将其作为将在生产中禁用的断言。因为这将是无用的。使其成为引发异常的普通代码。确保在适当的情况下对其进行记录。确保在 UI 上显示错误。并确保您能够获取异常和日志以进行调查。

重要的是总有一天,一些用户会做一些使警告弹出的事情。无论是因为代码写得不好还是其他原因,你都会看到它。这意味着,与其花 2 天时间找出程序出现这种奇怪行为的原因,您将能够在起点使用完整的堆栈跟踪并在 2 小时内纠正问题。

带有禁用断言的检查与根本没有检查相同。它是您必须编写、阅读和维护的代码……毫无意义。我理解整个性能论点,其中生产中的断言会减慢速度。是的,在少数情况下,存在性能问题。在大多数情况下,无论如何,您几乎一无所获,并且失去了宝贵的提示。

于 2011-05-16T08:32:16.033 回答
1

在公共方法中使用断言完全没有错。它们可用于检查某些不变量(您认为是真实的事情),关于您调用其方法的对象或类 - 实际上是真实的。

例如,您可以在构建器的公共 build() 方法中使用我所做的断言,以确保构建器由我自己在该类中的内部代码正确初始化(因为我有许多重载的构造函数用于它)。

但是你永远不应该做的是使用断言来检查公共方法的 ARGUMENTS。一个重要的区别。我认为这里的其他答案已经足够清楚地解释了原因,所以我不会重复任何事情。

于 2014-06-30T14:08:03.473 回答
0

这个想法是你不知道谁会使用你的公共方法。因此,您必须通过正常检查来保护自己免受不当使用。

另一方面,私有方法应该仅供您当前团队的开发人员使用,因此检查不是(那)强制性的(但仍然建议恕我直言)。

因此,对于检查私有方法的参数有效性,断言应该足够了。

于 2011-03-23T11:55:22.263 回答
0

此禁令仅适用于公共接口。

http://download.oracle.com/javase/6/docs/technotes/guides/language/assert.html#preconditions

按照惯例,公共方法的先决条件是通过显式检查来强制执行的,这些检查会抛出特定的、指定的异常。

基本上,约定是公共接口保证检查前置条件并抛出特定异常而不是 AssertionError。

对于所有其他情况,断言非常有价值,并且是“按合同编程”的基石。请参阅http://java.sun.com/developer/technicalArticles/JavaLP/assertions以获得很好的介绍。

  1. Java 有未经检查的异常是有原因的——这些是很多通常不应该被捕获的灾难性故障。任何内存分配都可能引发 OutOfMemoryError。一个失败的断言(客户端代码中的一个错误为我们的 API 提供了一个无效的参数)同样是灾难性的。

  2. 确实可以关闭断言。但是,这绝不应该这样做。只是不要这样做。如果您担心运行您的代码的人会关闭断言,您总是可以创建自己的一个无法关闭的琐碎断言类。使用断言的原则没有改变。

  3. 关于断言,您应该考虑的唯一事情是接口上的性能合同。请注意,这也可能是一个“隐式”合同(即,当一个明显的实施应该非常快速时,花一分钟是在隐含的性能合同之外)。因此,请确保验证您的断言在性能合同下是可接受的。

于 2011-05-18T18:09:58.150 回答
0

任何人都可以调用公共方法,并且无法控制可以作为参数值传递的内容。

如果假设在公共方法中我们已经使用断言验证了输入参数值,那么如果断言被禁用,那么这些检查(验证)可能不会发生(或执行)并且我们会从方法执行中得到不希望的结果。为了避免这种不良结果,我们不应该使用断言来验证公共方法的参数值。

现在您可能会想到的问题是,为什么要使用断言来验证私有方法的参数值?

嗯,原因是私有方法可以从它定义的类中调用(从实例方法或静态主方法)。类的开发人员实际上知道关于私有方法的一切——它做什么,如何调用它并传递什么参数值。因此可以使用断言安全地验证私有方法参数值。

于 2017-03-14T10:43:53.897 回答