为什么断言不能用于公共方法中的参数检查?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么断言不能用于公共方法中的参数检查?相关的知识,希望对你有一定的参考价值。

好吧,我在java的有限经验中从未真正使用过断言,并且想知道为什么我在许多网站上阅读过很多关于断言的书籍,同样的警告断言语句不应该用于参数检查公共方法?我想知道这是否与assert语句相对于java中其他语句的执行顺序有关。

答案

断言的目的是检查程序逻辑 - 断言失败是“停止一切 - 有一个错误!”指示。特别是,断言失败表示“这里有一个错误”,但“HERE”位于代码内部的某个地方,失败的原因只能通过检查您的代码(API的用户不能和应该这样做)来确定。不应该这样做)。

当你在API中获得错误的数据时,你想表明“嘿!你给了我糟糕的数据!” IllegalArgumentException及其亲属是表明这一点的方式。

(但请注意,对代码中的参数使用断言检查没有任何问题 - 您不支持团队外部人员使用的真正“公共”API。)

但这确实提出了另一个观点:在合理/可能的范围内,您应该“捕获”由于您自己的错误而可能发生的IllegalArgumentException的内部异常,并将它们转换为FatalError异常或类似的,因此您的API的用户当您的代码中存在错误时,不会导致他在寻找错误的参数。

(另请注意public - Java关键字和“公共接口”之间的区别 - 这意味着某些界面可以作为“正式”API提供给编程团队以外的人使用。后一种情况我们在这里担心。)

另一答案

非正式地,参数检查和断言用于不同的目的:

  • 参数检查是检测调用方法的人做错了什么的情况
  • 断言用于检测错误执行操作时的情况。

基本上,当你断言一个条件

assert val < total;

检查向你的代码的读者传达了以下简单的英语思想:“我检查了我的代码,根据我的推理,我确信val总是小于total”。

另一方面,当你检查一个参数val时,

if (val >= total) throw new InvalidArgumentException("val");

你的代码说“调用者忘记确保val小于total”。

这是两种不同的想法,因此很自然地采用两种不同的方式在代码中传达它们。

另一答案

根据programming with Assertions

参数检查通常是方法的已发布规范(或契约)的一部分,无论是启用还是禁用断言,都必须遵守这些规范。使用断言进行参数检查的另一个问题是错误的参数应该导致适当的运行时异常(例如IllegalArgumentException,IndexOutOfBoundsException或NullPointerException)。断言失败不会引发适当的异常。

另一答案

首先,Java断言在运行时被删除,除非它们在编译时被显式启用。

异常更适合参数验证,因为你期望处理它们,而断言具有语义含义“这在代码中的这一点必须是真的,否则我不知道如何处理它”。

另一答案

因为在生产版本中禁用了断言。如果您需要能够在错误地使用公共方法时捕获,那么断言将不会触发生成构建中的检查,并且异常将是发出错误信号的更好方法。

这对于库来说尤为重要,因为您无法控制调用方法的方式和方式;对于应用程序,公共方法中的断言是正常的,因为当你有正确的输入验证时(其中“输入”可以是用户输入,或来自另一个系统或来自持久存储的输入),那么断言应该永远不会被触发。

另一答案

不幸的是,迈克给出了一个很好的答案,在Java文献中很少得到辩护。对不起,我在这里没有足够的声誉来支持它。相反,我会提供自己的答案,我希望能为Mike的观点增加一些额外的支持。

绝大多数Java文献都传播了你不应该使用assert来检查公共方法参数的教条。换句话说,他们说assert不应该用于检查公共方法的前提条件,而是应该使用明确的if (!precond) throw SomeException();指令。标准Java库中充满了此策略的示例。

支持这一点的论据似乎是:

  • 满足前提条件的责任在于函数调用者(客户端代码),因此不是“您的”代码(供应商代码)。
  • 断言检查是可选的,因此您最好强制检查。

嗯,这对我来说似乎是一种非常光顾的态度。您的代码的客户端是程序员,就像您一样。满足前提条件的是客户的责任,当然,如果客户不这样做,那就是一个错误;客户的错误,而不是你的。当然,您希望您的客户使用启用的断言来检查他们的程序,不是吗?但是一旦他们确信他们的程序是正确的,为什么还要强制你的无用例外呢?

现在,从客户的角度来看。你正在使用String.charAtdocumentation for this method告诉你

public char charAt(int index)

返回指定索引处的char值。索引范围从0到length() - 1. [...]

明确说明了前提条件。但后来它补充道

抛出:IndexOutOfBoundsException - 如果index参数为负数或不小于此字符串的长度。

所以,如果你确定你的索引在界限范围内,你还会将你的调用放在try ... catch中吗?请注意,如果您尊重前提条件,文档并不会真正告诉您不会抛出异常,但当然这是您所期望的,不是吗?

所以,你确定索引是在界限范围内,你甚至可能断言以前,你不会浪费时间和空间与无用的try永远不会捕获任何东西,但String仍然会浪费时间检查你表现得很好。

我看的方式,

  • 断言应该用于检查完全由程序员控制的条件,无论是客户还是供应商。
  • 应使用带有异常的显式条件指令(或错误返回代码之类的其他错误信号设备)来检查依赖于无法控制的外部因素的条件,例如用户输入,不可预测的操作系统限制等。

这就是我教给学生的原因。我不告诉他们。这不是教条。我让他们知道这与大多数书中的内容相反,但我敦促他们自己决定。

这个问题不是针对Java的,而是一个关于编程方法的问题。我遵循的方法类似于Design by Contract。某些语言具有支持此样式的特殊语法,例如显式声明前置条件,后置条件和对象不变量的可能性,这些明确包含在代码的公共规范(和文档)中。

若奥罗德里格斯

另一答案

断言不应该用于公共方法中的参数检查这一概念是完全错误的。仅仅因为你在书中读到的东西并不意味着它是正确的。

公共方法中的参数检查完全属于检查错误的一般类别,因此它应该被视为这样,并且我们已经用于捕获错误的机制是断言。

如果方法的公共接口说“此方法的'index'参数永远不应该是负数”,那么使用负索引调用它是一个错误,并且以下情况适用:

  1. 它绝不应该发生在生产环境中。 (测试应该保证这一点。)
  2. 即使它是在生产环境中发生的,也没有任何人可以做任何事情来缓解这个问题,因此它可能会因索引超出范围或空指针异常而进一步失败,它没有任何区别。
  3. 没有人应该依赖那里的支票。 (故意允许使用无效参数调用该方法,捕获生成的IllegalArgumentException,并尝试采取纠正措施是一个坏主意。)

事实上,保证特定异常即使在生产中也会被抛出,因为你认为这是一个错误的条件会诱使n00b程序员编写代码,这些代码将依赖于生产中抛出的异常,因此,本身就是一个错误。

错误也是一个断言表示“在这里”的错误的概念。如果断言检查方法参数,则断言捕获的错误可以在堆栈跟踪中列出的方法调用链的任何位置,或者甚至可能在其他地方。

那里有数十亿台设备,其中大部分都是以宝贵的电池供电,每天执行数以万亿计的指令,这些设备只是针对数百万工时测试所保证绝不会发生的情况进行测试。开发人员和测试人员。这太疯狂了。

所以,简单的assert适用于参数检查。如果您需要编写测试代码以确保您的方法确实正确地针对特定的错误条件进行断言,请考虑以下构造:

assert index >= 0 : new IllegalArgumentException( "index" );

显然,如果断言被启用,这只会执行测试,但它真正的美妙之处在于,如果断言失败,那么cause异常的AssertionError将是IllegalArgumentException,因此您的测试代码将能够确保正确错误被抓住了。

有关此主题的更多信息,请参阅我的博客上的这篇文章:michael.gr - Assertions and testing

以上是关于为什么断言不能用于公共方法中的参数检查?的主要内容,如果未能解决你的问题,请参考以下文章

Java基础之断言

Effective Java中的方法部分

Jmeter之断言——检查点

jmeter断言

JMeter断言

什么是断言?你为什么要使用它们?