在Groovy中使用Guava预条件而不是使用assert来约束方法参数有什么负面影响?

在Groovy中使用Guava预条件而不是使用assert来约束方法参数有什么负面影响?,groovy,guava,Groovy,Guava,我知道,用于检查方法前提条件的典型Groovy习惯用法(例如要求参数为非null或在可接受值的特定范围内)通常是使用assert 然而,在我看来,这种方法与使用熟悉的(无论如何对我来说)依赖Java的Java方法相比,似乎有几个缺点 如果断言失败,将始终抛出java.lang.AssertionError。这远不如说,对失败的空检查抛出NullPointerException,对其他参数问题抛出IllegalArgumentException,这可以通过先决条件轻松实现。如果出于任何原因,我想

我知道,用于检查方法前提条件的典型Groovy习惯用法(例如要求参数为非null或在可接受值的特定范围内)通常是使用
assert

然而,在我看来,这种方法与使用熟悉的(无论如何对我来说)依赖Java的Java方法相比,似乎有几个缺点

  • 如果断言失败,将始终抛出
    java.lang.AssertionError
    。这远不如说,对失败的空检查抛出
    NullPointerException
    ,对其他参数问题抛出
    IllegalArgumentException
    ,这可以通过
    先决条件轻松实现。如果出于任何原因,我想尝试捕获一个错误的方法调用,那么调用代码将很难理解该方法在
    断言错误中失败的确切原因
  • 如果我以后在方法中使用
    assert
    进行任何其他类型的检查(例如验证类的状态不变量),调用方将再次更难确定它接收到的
    AssertionError
    的含义,与接收一个
    IllegalStateException
    或我的应用程序特有的一些更具体的自定义子类型相比。失败是打电话的人的错吗?或者是所谓的类错误及其内部状态混乱
  • 调试失败的
    先决条件
    检查更容易-我可以在抛出的
    IllegalArgumentException
    或其他任何地方的每个方法调用中设置一个断点,并知道它是否被命中。或者,我可以在抛出特定异常类型时打开断点(即,只是
    IndexOutOfBoundsException
    ,而不是任何断言失败),以仅捕获当前感兴趣的前提条件失败的情况
  • 人们以后更容易阅读和解释代码。调用
    预条件
    必须是预条件检查,并且调用的特定方法提供了更直接的信息。断言可以检查任何内容
关于使用
assert
的唯一优点,我可以看到,它不必像下面的
ConstraintWithPremissions2
那样手动添加错误参数值的打印,这稍微方便了一点:

@Grab(group='com.google.guava', module='guava', version='18.0')
import com.google.common.base.Preconditions

static String constrainWithPreconditions(def param1) {
    Preconditions.checkNotNull(param1, 'param1 can NOT be null!')
}

static String constrainWithAssertions(def param1) {
    assert param1, 'param1 can NOT be null!'
}

// constrainWithPreconditions(null)
// PRODUCES:
// java.lang.NullPointerException: param1 can NOT be null!

// constrainWithAssertions(null)
// PRODUCES:
// java.lang.AssertionError: param1 can NOT be null!. Expression: param1. Values: param1 = null

static String constrainWithPreconditions2(def param1, def param2) {
    Preconditions.checkArgument(param1 < param2, "param1 ($param1) MUST be < param2 ($param2)!")
}

static String constrainWithAssertions2(def param1, def param2) {
    assert param1 < param2, 'param1 MUST be < param2!'
}

 constrainWithPreconditions2(2, 1)
// PRODUCES:
// java.lang.IllegalArgumentException: param1 (2) MUST be < param2 (1)!

// constrainWithAssertions2(2, 1)
// PRODUCES:
// ava.lang.AssertionError: param1 MUST be < param2!. Expression: (param1 < param2). Values: param1 = 2, param2 = 1
@Grab(group='com.google.guava',module='guava',version='18.0')
导入com.google.common.base.premissions
静态字符串constraintWithPremissions(def param1){
前提条件。checkNotNull(param1,'param1不能为null!')
}
静态字符串constraintwithassertions(def param1){
断言param1,“param1不能为null!”
}
//constraintWithPremissions(null)
//产生:
//java.lang.NullPointerException:param1不能为null!
//ConstraintWithAssertions(null)
//产生:
//java.lang.AssertionError:param1不能为null!。表达式:param1。值:param1=null
静态字符串constraintWithPremissions2(定义参数1、定义参数2){
前提条件。checkArgument(param1

那么我错过了什么?
先决条件是否还有其他缺点?我是否因为没有使用
assert
而遗漏了其他内容?

我更喜欢先决条件,原因与您提到的相同。是的,没错。无需使用
断言
如果
前提条件
可用-良好的库。断言和前提条件检查是完全不同的事情。断言是您相信/期望在代码中始终为真的东西;先决条件检查是检查您无法控制的内容(方法的参数)是否符合您设置的方法调用要求。@ColinD-我完全同意这一点。然而,我一直看到的习惯用法似乎是使用断言来强制前提条件检查。因此,这个成语是非常错误的。节省几笔笔划对你自己提出的论点没有任何影响。我想补充一点,您将失去为创建资产的目的使用资产的能力,也就是说,可能需要耗时的健全性检查。我经常使用断言,当它们出现时,减速10倍并不罕见。还要注意的是,番石榴中有
Verify
,用于那些必须经常检查并且没有先决条件的东西。