JetBrains的@Contract注释

时间:2016-01-05 20:17:57

标签: java intellij-idea jetbrains-ide

org.jetbrains.annotations.Contract注释如何工作? IntelliJ IDEA如何支持它?

3 个答案:

答案 0 :(得分:41)

首先,我应该说这个注释仅供IDEA用于检查可能的错误。 Java编译器几乎完全忽略它(它将在编译的工件中但不起作用)。说完了......

注释的目标是描述方法将遵循的契约,这有助于IDEA捕获可能调用此方法的方法中的问题。有问题的合同是一组分号分隔条款,每个条款都描述了保证发生的输入和输出。因果由->分隔,并描述了当您向方法提供X时,Y将始终结果。输入被描述为以逗号分隔的列表,描述了多个输入的情况。

可能的输入为_(任意值),null!null(非空),falsetrue,可能的输出添加{ {1}}到此列表。

例如,fail表示,如果提供null -> false输入,则结果为null布尔值。 false对此进行了扩展,表示null -> false; !null -> true始终返回null而非false值将始终最后,null表示如果传递一个空值,该方法将抛出异常。

对于多参数示例,null -> fail表示在双参数方法中,如果第一个参数为null而第二个参数不为null,则会抛出异常,保证。

如果方法没有改变对象的状态,只返回一个新值,那么你应该将null, !null -> fail设置为true。

答案 1 :(得分:23)

official documentation指定注释的所有支持和识别值的formal grammar

通俗地说:

  • 合同可以包含1个或多个与之相关的条款
  • 一个条款始终 [args] -> [effect]
  • Args是一个或多个约束,定义为any | null | !null | false | true
  • 效果只是一个约束或fail

让我们来看一个简单的例子 - 我的最爱之一是,"无论你传入这个方法,它都会抛出异常。"

@Contract("_-> fail")
public void alwaysBreak(Object o) {
    throw new IllegalArgumentException();
}

在这里,我们使用_或"任何"来表示无论我们传入此方法的是什么,我们都会抛出异常。

如果我们说谎并且说这种方法将无条件地返回true怎么办?

@Contract("_-> true")
public void alwaysBreak(Object o) {
    throw new IllegalArgumentException();
}

IntelliJ提出了一些警告。

enter image description here

当我们在 void 方法中...我们说我们正在返回一个布尔值时(显然)感到不安...

enter image description here

您发现自己想要使用@Contract的主要时间是:

  • 您希望保证您返回true或false
  • 您希望保证在给定约束的情况下返回非空值
  • 您希望明确表示您可以在给定约束的情况下返回空值
  • 您希望明确表示您将在约束条件下抛出异常

这并不是说@Contract是完美的;离得很远。它不能在某些情况下进行非常深入的分析,但在代码库中使用它可以让您的工具免费进行这种分析。

答案 2 :(得分:16)

  

org.jetbrains.annotations.Contract注释如何工作?

虽然之前的答案是提供信息的,但我不认为他们在你的问题中解决了“工作”这个词。也就是说,他们没有解释IntelliJ在幕后做什么来实现他们的注释,这样你就可以从头开始轻松地构建自己的注释。

如果您浏览下面的源代码,您可能会认为对于像@NotNull那样简单的声音,它似乎有点过于复杂(或者至少是冗长的)。我同意你的观点,这也是我一般避免使用@Contract的一个原因 - 就像那些不是“简单而简单”的条款(如@NotNull)而是直接使用JavaDoc我的先决条件。

通常不鼓励使用复杂的合同注释 - 尽管我可能会因为跳过这条时髦的新火车而受到仇恨 - 这里有一些原因:

  • 注释可能很复杂 -e.g。在他们自己的定义中有多个嵌套注释和/或外观为Turing completeness的“语法”。通过掩盖隐藏/抽象层背后的错误的真正罪魁祸首并且无法生成最初预期的警告,这可能导致编译时的错误信任感。
  • 与我之前的观点相似但不同,注释通常会在少数关键字中隐藏开发人员的大量逻辑,从而产生难以理解的人类代码和/或难以调试的异常行为
  • 应用配置通常是seen masquerading作为注释。看看Spring framework
  • 定义合同的语法很大(恕我直言)非常难看,Makefile-ish。例如,看一下JetBrains注释定义和支持文件{{3 }}。注意众多的XML文件和大量的自我引用?我写作和支持很难说,特别是考虑到Android和更大的Java社区之间来回的注释不断演变的本质。

需要考虑的一些问题:

  • 当注释的源代码接近它注释的源代码的复杂性时,是否会走得太远?
  • 下面的代码段是否真的比在运行时检查null 并使用stacktraces记录异常要好得多?包含类似的内容会强迫用户阅读,理解并可能修复另一组定义注释的依赖项。

借用scattered across its repo关于契约语义的借词,我认为只会进一步说明我的观点:

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
{
}

您何时以及应使用哪些合同?

我建议坚持从他们的名字中明确表达其意图的那种,并避免那些具有他们自己的语义和语言定义的人。

一个使用的例子 - 尽管是前一个片段 - 是@NotNull,但是当所有对象参数必须为空时,它仍然受限。

要避免的排序的示例是Android和IntelliJ的@Contract(...)。虽然我确实喜欢他们的IDE,但他们的注释细节非常复杂,最终变成了更多问题的根源和平台不兼容性让我追踪(不可否认的是,由于我几乎100%的时间都在创作新合同时无知,但为什么要把它弄好是如此困难?)

摘要/结论

注释是一个很好的想法,清楚地由希望“编纂”其文档的程序员生成。我觉得他们最近走得太远了,把文档变成了带语义的代码,导致了一些严重的难题和尴尬的情况。更糟糕的是,它们有时会因为未能检测到自己实现中出现的问题而赋予编译时安全性的错误感。坚持非常简单,避免任何看起来像非Java语言的东西(这是你打算首先写的)。

进一步阅读

这个简短的列表是来自StackOverflow和网络的大多数关键(w /乐观!)来源的混合,我觉得这些来源说明了我的一些观点。

没有特别的顺序:

毕竟,我刚刚意识到我可能仍未能完整解决你原来的问题:)