Java JetBrains 的 @Contract 注释
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34620494/
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
JetBrains' @Contract annotation
提问by Francesco Menzani
How does the org.jetbrains.annotations.Contract
annotation work?
How does IntelliJ IDEA support it?
org.jetbrains.annotations.Contract
注释是如何工作的?IntelliJ IDEA 如何支持它?
采纳答案by dcsohl
First off, I should say that this annotation is only for IDEA to use to check for possible errors. The Java compiler will ignore it almost entirely (it'll be in the compiled artifact but have no effect). Having said that...
首先,我应该说这个注释仅供 IDEA 用来检查可能的错误。Java 编译器将几乎完全忽略它(它将在已编译的工件中,但没有任何效果)。话说回来...
The goal of the annotation is to describe a contractthat the method will obey, which helps IDEA catch problems in methods that may call this method. The contract in question is a set of semi-colon separated clauses, each of which describes an input and an output that is guaranteed to happen. Cause and effect are separated by ->
, and describe the case that when you provide X to the method, Y will alwaysresult. The input is described as a comma-separated list, describing the case of multiple inputs.
注解的目的是描述一个方法将遵守的契约,这有助于 IDEA 捕捉可能调用此方法的方法中的问题。有问题的合同是一组以分号分隔的条款,每个条款都描述了保证发生的输入和输出。因果由->
,分隔,并描述当您向方法提供 X 时,Y 将始终产生的情况。输入被描述为逗号分隔的列表,描述了多个输入的情况。
Possible inputs are _
(any value), null
, !null
(not-null), false
and true
, and possible outputs adds fail
to this list.
可能的输入是_
(任何值)、null
、!null
(非空)false
和true
,并且可能的输出添加fail
到此列表中。
So for example, null -> false
means that, provided a null
input, a false
boolean is the result. null -> false; !null -> true
expands on this to say that null
will alwaysreturn false
and a non-null
value will alwaysreturn true, etc. Finally, null -> fail
means the method will throw an exception if you pass it a null value.
例如,null -> false
意味着,提供一个null
输入,false
结果是一个布尔值。null -> false; !null -> true
对此进行扩展,表示null
将始终返回false
,而非null
值将始终返回 true,等等。最后,null -> fail
意味着如果您向它传递空值,该方法将引发异常。
For a multiple-argument example, null, !null -> fail
means that, in a two-argument method, if the first argument is null and the second is not null, an exception will be thrown, guaranteed.
对于多参数示例,null, !null -> fail
意味着,在双参数方法中,如果第一个参数为 null,第二个参数不为 null,则保证会抛出异常。
If the method does not change the state of the object, but just returns a new value, then you should set pure
to true.
如果该方法不改变对象的状态,而只是返回一个新值,那么您应该设置pure
为 true。
回答by Makoto
The official documentationspecifies the formal grammarof all supported and recognized values for the annotation.
In layman's terms:
通俗地说:
- A contract can have 1 or more clauses associated with it
- A clause is always
[args] -> [effect]
- Args are 1 or more constraints, which are defined as
any | null | !null | false | true
- Effects are only one constraint or
fail
- 一个合同可以有 1 个或多个与之相关的条款
- 条款总是
[args] -> [effect]
- Args 是 1 个或多个约束,定义为
any | null | !null | false | true
- 效果只是一种约束或
fail
Let's run through a quick example - one of my favorites is, "Whatever you pass into this method, it will throw an exception."
让我们来看一个简单的例子——我最喜欢的一个例子是,“无论你传递给这个方法什么,它都会抛出一个异常。”
@Contract("_-> fail")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
Here, we're using _
, or "any", to indicate that regardless of what we pass into this method, we're going to throw an exception.
在这里,我们使用_
或“any”来表示无论我们传递给这个方法的内容如何,我们都将抛出异常。
What if we lied and said that this method was going to return true
unconditionally?
如果我们撒谎说这个方法会true
无条件返回怎么办?
@Contract("_-> true")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
IntelliJ raises some warnings about it.
IntelliJ 对此提出了一些警告。
It's also (obviously) upset that we said we were returning a boolean when we're in a voidmethod...
当我们在void方法中时,我们说我们要返回一个布尔值也(显然)令人不安......
The main times you'll find yourself wanting to use @Contract
is when:
您会发现自己想要使用的主要时间@Contract
是:
- You want to guarantee that you return true or false
- You want to guarantee that you return a non-null value given constraints
- You want to make clear that you can return a null value given constraints
- You want to make clear that you will throw an exception given constraints
- 你想保证你返回真或假
- 您想保证在给定约束的情况下返回非空值
- 您想明确表示您可以在给定约束的情况下返回空值
- 您想明确表示您将在给定约束的情况下抛出异常
That's not to say that @Contract
is perfect; far from it. It can't do very deep analysis in certain contexts, but having this in your code base allows your tooling to do this sort of analysis for free.
这并不是说这@Contract
是完美的。离得很远。它不能在某些上下文中进行非常深入的分析,但是在您的代码库中使用它可以让您的工具免费进行此类分析。
回答by EntangledLoops
How does the
org.jetbrains.annotations.Contract
annotation work?
org.jetbrains.annotations.Contract
注释是如何工作的?
Although the previous answers are informative, I don't feel they address the operative word "work" in your question. That is, they don't explain what IntelliJ is doing behind the scenes to implement their annotations so that you could build your own easily from scratch.
尽管之前的答案内容丰富,但我认为它们并没有解决您问题中的“工作”一词。也就是说,他们没有解释 IntelliJ 在幕后做了什么来实现他们的注释,以便您可以轻松地从头开始构建自己的注释。
If you glance at the source code below, you might think that it seems a bit overly complicated (or at least verbose) for something as simple-sounding as @NotNull
. I would agree with you, and it's one reason I generally avoid @Contract
-like clauses that aren't "plain and simple" (like @NotNull
) and instead JavaDoc my prereqs directly.
如果您看一下下面的源代码,您可能会认为对于像@NotNull
. 我同意你的看法,这也是我通常避免使用@Contract
不“简单明了”(如@NotNull
)的类似子句的原因之一,而是直接使用 JavaDoc 我的先决条件。
I generallydiscourage using complex contract annotations—despite the hate I might receive for skipping this trendy new train—and here some reasons why:
我通常不鼓励使用复杂的合同注释——尽管我可能会因为跳过这条时髦的新列车而受到憎恨——这里有一些原因:
- Annotations can be complex—e.g. having multiple nested annotations in their own definitions and/or a "grammar" with the appearance of Turing completeness. This can lead to a false sense of confidence at compile-time by masking the true culprit of a bug behind layers of obscurity/abstraction and failing to generate the originally intended warnings.
- Similar but different than my previous point, annotations often hide copious logic from the developerin a handful of keywords, producing difficult to understand code for humans and/or unusual behavior that can be difficult to debug.
- App configuration is often seen masqueradingas annotations. Have a look at the Spring framework.
- The syntax for defining contracts is by-and-large (IMHO) quite ugly and Makefile-ish.For example, take a glance at some of the JetBrains annotation definitions and supporting files scattered across its repo. Notice the numerous XML files and copious self-reference? I'd hardly call that fun to write and support, especially considering the constantly evolving nature of annotations headed by the back-and-forth between Android and the larger Java community.
- 注释可能很复杂——例如,在它们自己的定义和/或具有图灵完整性外观的“语法”中有多个嵌套的注释。这可能会导致在编译时产生错误的信心,因为它掩盖了隐藏/抽象层背后的错误的真正罪魁祸首,并且无法生成最初预期的警告。
- 与我之前的观点类似但不同的是,注解通常会在少数几个关键字中向开发人员隐藏大量逻辑,从而产生难以理解的人类代码和/或难以调试的异常行为。
- 应用程序配置经常被视为伪装成注释。看看Spring 框架。
- 定义合约的语法大体上(恕我直言)非常丑陋和 Makefile-ish。例如,看一眼分散在其 repo中的一些 JetBrains 注释定义和支持文件。注意到大量的 XML 文件和大量的自我参考吗?我几乎不会把编写和支持称为乐趣,特别是考虑到由 Android 和更大的 Java 社区之间的来回主导的注释不断发展的性质。
Some questions to consider:
一些需要考虑的问题:
- Is it going too far when an annotation's source code approaches the complexity of the very source it annotates?
- Is the code segment below really that much better then just checking for null at runtimeand logging exceptions with stacktraces? Including something like that forces your users to read through, comprehend, and possibly bug-fix another set of dependencies that define your annotations.
- 当注解的源代码接近它所注解的源代码的复杂性时,这是否太过分了?
- 下面的代码段真的比在运行时检查 null并使用堆栈跟踪记录异常要好得多吗?包含类似的内容会迫使您的用户通读、理解并可能修复定义您的注释的另一组依赖项。
Borrowed from yet another lengthy postabout contract semantics that I would argue only further serves my point:
借用另一篇关于合约语义的长篇文章,我认为这只会进一步说明我的观点:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
/**
* This annotation can be applied to a package, class or method to indicate that the class fields,
* method return types and parameters in that element are not null by default unless there is: <ul>
* <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
* case the annotation of the corresponding parameter in the superclass applies) <li> there is a
* default parameter annotation applied to a more tightly nested element. </ul>
* <p/>
* @see https://stackoverflow.com/a/9256595/14731
*/
@Documented
@Nonnull
@TypeQualifierDefault(
{
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.LOCAL_VARIABLE,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.PARAMETER,
ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNullByDefault
{
}
When and what contracts should you use?
你应该在什么时候使用什么合同?
I suggest sticking to the kind whose intentions are crystal-clear from their name, and avoid those with their own set of semantics and language-like definitions.
我建议坚持使用其意图从其名称中一目了然的那种,并避免那些具有自己的语义和类似语言定义的集合。
An example of one to use—in spite of the previous segment—is @NotNull
, but keep it limited to when all object parameters must be null.
一个要使用的示例(尽管有前一段)是@NotNull
,但将其限制在所有对象参数必须为空的情况下。
An example of the sort to avoidare those like Android and IntelliJ's @Contract(...)
. While I do love their IDEs, the details of their annotations are quite complicated and ultimately turned into a source of more problems and platform incompatibility for me to track down (admittedly caused by my own ignorance in authoring new contracts almost 100% of the time, but why is it so difficult to get it right?)
要避免的排序的一个例子是像 Android 和 IntelliJ 的@Contract(...)
. 虽然我很喜欢他们的 IDE,但他们的注释细节相当复杂,最终变成了更多问题和平台不兼容的根源供我追踪(不可否认,这是由于我自己几乎 100% 的时间都在编写新合同的无知,但是为什么做对这么难呢?)
Summary / Conclusion
总结/结论
Annotations are a great idea clearly generated by programmers looking to "codify" their documentation. I feel they've gone too far lately, turning documentation into semantics-bearing code that leads to some serious conundrums and awkward situations. Even worse, they sometimes endow a false sense of compile-time safety by failing to detect issues manifest in their own implementations. Stick to the very simple and avoid anything that looks like a language that isn't Java (which is what you intended to be writing in the first place).
注释是希望“编纂”他们的文档的程序员清楚地产生的一个好主意。我觉得他们最近走得太远了,将文档变成了承载语义的代码,这导致了一些严重的难题和尴尬的情况。更糟糕的是,他们有时会因为未能检测到他们自己的实现中出现的问题而给人一种编译时安全的错误感觉。坚持非常简单,避免任何看起来像不是 Java 的语言(这是您最初打算编写的语言)。
Further Reading
进一步阅读
This brief list is a mix of mostly critical (w/optimism!) sources from both StackOverflow and the web that I feel illustrate some of my points.
这个简短的列表混合了来自 StackOverflow 和网络的大部分关键(带有乐观!)来源,我认为它们说明了我的一些观点。
In no particular order:
没有特定的顺序:
- Java Annotations are a Big Mistake
- Annotations, Annotations, Everywhere
- Arguments against annotations
- Are annotations bad?
- An Annotation Nightmare
- Evil Annotations
And after all that, I just realized I may still have failed to address your original question in its entirety :)
毕竟,我才意识到我可能仍然没有完整地解决您最初的问题:)