java 如何在我的方法输入参数上设置验证约束?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1809093/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:05:14  来源:igfitidea点击:

How can I place validating constraints on my method input parameters?

javavalidationannotationsdesign-by-contractcontract

提问by Robert Campbell

Here is the typical way of accomplishing this goal:

以下是实现此目标的典型方法:

public void myContractualMethod(final String x, final Set<String> y) {
    if ((x == null) || (x.isEmpty())) {
        throw new IllegalArgumentException("x cannot be null or empty");
    }
    if (y == null) {
        throw new IllegalArgumentException("y cannot be null");
    }
    // Now I can actually start writing purposeful 
    //    code to accomplish the goal of this method

I think this solution is ugly. Your methods quickly fill up with boilerplate code checking the valid input parameters contract, obscuring the heart of the method.

我认为这个解决方案很丑陋。你的方法很快就会被样板代码填满,检查有效的输入参数合同,掩盖了方法的核心。

Here's what I'd like to have:

这是我想要的:

public void myContractualMethod(@NotNull @NotEmpty final String x, @NotNull final Set<String> y) {
    // Now I have a clean method body that isn't obscured by
    //    contract checking

If those annotations look like JSR 303/Bean Validation Spec, it's because I borrowed them. Unfortunitely they don't seem to work this way; they are intended for annotating instance variables, then running the object through a validator.

如果这些注释看起来像 JSR 303/Bean Validation Spec,那是因为我借用了它们。不幸的是,他们似乎不是这样工作的。它们用于注释实例变量,然后通过验证器运行对象。

Which of the many Java design-by-contract frameworksprovide the closest functionality to my "like to have" example? The exceptions that get thrown should be runtime exceptions (like IllegalArgumentExceptions) so encapsulation isn't broken.

众多 Java 契约式设计框架中,哪一个提供了与我的“喜欢拥有”示例最接近的功能?抛出的异常应该是运行时异常(如 IllegalArgumentExceptions),因此封装不会被破坏。

采纳答案by Jared Russell

If you're looking for a fully fledged design-by-contract mechanism I'd take a look at some of the projects listed on the Wikipedia page for DBC.

如果您正在寻找完全成熟的按合同设计机制,我会查看DBC 维基百科页面上列出的一些项目。

If your looking for something simpler however, you could look at the Preconditionsclass from google collections, which provides a checkNotNull() method. So you can rewrite the code you posted to:

但是,如果您正在寻找更简单的东西,您可以查看谷歌集合中的Preconditions类,它提供了一个 checkNotNull() 方法。所以你可以重写你发布的代码:

public void myContractualMethod(final String x, final Set<String> y) {
    checkNotNull(x);
    checkArgument(!x.isEmpty());
    checkNotNull(y);
}

回答by Michael Easter

I've seen a technique by Eric Burkethat is roughly like the following. It is an elegant use of static imports. The code reads very nicely.

我见过Eric Burke 的一种技术,大致如下所示。这是静态导入的优雅使用。代码读起来非常好。

To get the idea, here is the Contractclass. It is minimal here, but can be easily filled out as needed.

为了得到这个想法,这是Contract课程。这里是最小的,但可以根据需要轻松填写。

package net.codetojoy;

public class Contract {
    public static void isNotNull(Object obj) {
        if (obj == null) throw new IllegalArgumentException("illegal null");
    }
    public static void isNotEmpty(String s) {
        if (s.isEmpty()) throw new IllegalArgumentException("illegal empty string");
    }
}

And here is an example usage. The foo()method illustrates the static imports:

这是一个示例用法。该foo()方法说明了静态导入:

package net.codetojoy;

import static net.codetojoy.Contract.*;

public class Example {
    public void foo(String str) {
        isNotNull(str);
        isNotEmpty(str);
        System.out.println("this is the string: " + str);
    }

    public static void main(String[] args) {
        Example ex = new Example();
        ex.foo("");
    }
}

Note: when experimenting, note that there may be a bugaround doing this in the default package. I've certainly lost brain cells trying it.

注意:在试验时,请注意在默认包中执行此操作可能存在错误。尝试它我肯定失去了脑细胞。

回答by Verhagen

There is a small Java Argument Validationpackage, implemented as Plain Java. It comes with several standard checks / validations. And for those cases where someone need its own more specific validations, it comes with some helper methods. For validations that occur multiple times, just extend the interface ArgumentValidation, with your own And create the implementing class that extends from the class ArgumentValidationImpl.

有一个小的Java Argument Validation包,实现为普通 Java。它带有几个标准检查/验证。对于那些需要自己更具体的验证的情况,它带有一些辅助方法。对于多次发生的验证,只需扩展接口 ArgumentValidation,并创建从类 ArgumentValidationImpl 扩展的实现类。

回答by GaryF

Not a fully working solution, but JSR-303 has a proposal for a method-level validation extension. Because it's just an extension proposal just now, implementations of JSR-303 are free to ignore it. Finding an implementation is a little more tricky. I don't think Hibernate Validator supports it yet, but I believe agimatec-validationhas experimental support. I've not used either for this purpose so I don't know how well they work. I'd be interested in finding out though, if someone gives it a go.

不是一个完全有效的解决方案,但 JSR-303 有一个方法级验证扩展的提议。因为现在只是一个扩展提案,JSR-303 的实现可以随意忽略它。找到一个实现有点棘手。我认为 Hibernate Validator 还不支持它,但我相信agimatec-validation有实验支持。我没有为此目的使用过它们,所以我不知道它们的效果如何。如果有人试一试,我很想知道。

回答by Erk

If you're using Java 8, lambdas can be used to create a very elegant and readable solution for validation:

如果您使用的是 Java 8,可以使用 lambdas 来创建一个非常优雅且可读的验证解决方案:

public class Assert {

    public interface CheckArgument<O> {
        boolean test(O object);
    }

    public static final <O> O that(O argument, CheckArgument<O> check) {
        if (!check.test(argument))
            throw new IllegalArgumentException("Illegal argument: " + argument);
        return argument;
    }
}

You use it like:

你像这样使用它:

public void setValue(int value) {
    this.value = Assert.that(value, arg -> arg >= 0);
}

The exception will look like:

异常将如下所示:

Exception in thread "main" java.lang.IllegalArgumentException: Illegal argument: -7
    at com.xyz.util.Assert.that(Assert.java:13)
    at com.xyz.Main.main(Main.java:16)

The first nice thing is that the above Assert class is all that's needed really:

第一件好事是上面的 Assert 类是真正需要的:

public void setValue(String value) {
    this.value = Assert.that(value, arg -> arg != null && !arg.trim().isEmpty());
}

public void setValue(SomeObject value) {
    this.value = Assert.that(value, arg -> arg != null && arg.someTest());
}

Of course that()can be implemented in a number of ways: with a format string and arguments, to throw other kinds of exceptions, etc.

当然that()可以通过多种方式实现:使用格式字符串和参数,抛出其他类型的异常等。

It does not, however, need to be implemented to perform different tests.

但是,不需要实施它来执行不同的测试。

Not that you can't pre package tests if you like to:

如果您愿意,并不是说您不能预先打包测试:

public static CheckArgument<Object> isNotNull = arg -> arg != null;

Assert.that(x, Assert.isNotNull);

// with a static import:

Assert.that(x, isNotNull);

I have no clue if this is bad for performance or not a good idea for some other reason. (Just started looking at lambdas myself but the code seems to run as it should...) But I like that Assertcan be kept short (no need for dependencies that may not be crucial to the project) and that the tests are very visible.

我不知道这是否对性能不利或由于其他原因不是一个好主意。(我自己刚开始研究 lambdas,但代码似乎按它应该的方式运行......)但我喜欢Assert可以保持简短(不需要可能对项目不重要的依赖项)并且测试非常明显。

Here's a method for a better error message:

这是一个更好的错误信息的方法:

public static final <O> O that(O argument, CheckArgument<O> check,
    String format, Object... objects) 
{
    if (!check.test(argument))
        throw new IllegalArgumentException(
            String.format(format, objects));
    return argument;
}

You call it like:

你这样称呼它:

public void setValue(String value) {
    this.value = Assert.that(value, 
        arg -> arg != null && arg.trim().isEmpty(), 
        "String value is empty or null: %s", value);
}

And out comes:

结果出来了:

Exception in thread "main" java.lang.IllegalArgumentException: String value is empty or null: null
    at com.xyz.util.Assert.that(Assert.java:21)
    at com.xyz.Main.main(Main.java:16)

Update:If you want to use the x = Assert...construction with a prepackaged test, the result will be cast to the type used in the prepackaged test. So it must be cast back to the type of the variable... SomeClass x = (SomeClass) Assert.that(x, isNotNull)

更新:如果您想将x = Assert...构造与预打包测试一起使用,结果将被强制转换为预打包测试中使用的类型。所以它必须被强制转换回变量的类型......SomeClass x = (SomeClass) Assert.that(x, isNotNull)

回答by srini.venigalla

I would use Parameter Annotations, Reflection and a generic validator class to create an app-wide facility. for example, you can code a class method like:

我会使用参数注释、反射和通用验证器类来创建应用程序范围的工具。例如,您可以编写类方法,例如:

.. myMethod( @notNull String x, @notNullorZero String y){

.. myMethod( @notNull String x, @notNullorZero String y){

if (Validator.ifNotContractual(getParamDetails()) {
    raiseException..
    or 
    return ..
}

}

}

The class methods are "marked up" to annotate their contract requirements. Use reflection to automatically discover the params, their values and annotations. Send it all to a static class to validate and let you know the outcome.

类方法被“标记”以注释它们的契约要求。使用反射自动发现参数、它们的值和注释。将其全部发送到静态类进行验证并让您知道结果。

回答by Stephen C

This doesn't directly answer your question, but I think that part of your problem is that you are overdoing the validation. For instance, you could replace the first test with:

这并不能直接回答您的问题,但我认为您的问题的一部分是您过度验证。例如,您可以将第一个测试替换为:

if (x.isEmpty()) {
    throw new IllegalArgumentException("x cannot be empty");
}

and rely on Java to throw a NullPointerExceptionif xis null. You just need to alter your "contract" to say that NPE is thrown for certain types of "you called me with illegal parameters" situations.

并依靠 Java 抛出NullPointerExceptionif xis null。您只需要更改您的“合同”,说明在某些类型的“您用非法参数给我打电话”的情况下会抛出 NPE。

回答by pmr

Jared pointed you to various frameworks that add support for DBC to Java.
What I found to work best is: simply document your contract in the JavaDoc (or whatever Documentationframework you use; Doxygen has support for DBC tags.)
Having your code obfuscated by a lot of throws and checks of your arguments isn't really helpful to your reader. Documentation is.

Jared 向您介绍了向 Java 添加 DBC 支持的各种框架。
我发现最有效的是:简单地在 JavaDoc(或您使用的任何文档框架;Doxygen 支持 DBC 标签)中记录您的合同。
通过大量抛出和检查您的参数来混淆您的代码并没有真正帮助你的读者。文档是。