1. 概述

本文将深入讲解如何使用 @Contract 注解。通过这个注解,我们可以为方法定义必须遵守的契约规则。该注解由 IntelliJ IDEA 的开发公司 JetBrains 推出,能让编辑器直接识别代码中调用方法时的潜在问题。

2. Maven 依赖配置

最新版本的 annotations 库可在 Maven 中央仓库 获取。pom.xml 中添加依赖:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>24.0.1</version>
</dependency>

3. value 属性详解

@Contract 注解包含两个属性:valuepure value 属性用于描述方法输入与输出之间的约束关系,这是注解的核心功能,首次出现在 IntelliJ 14 版本。

3.1. 契约语法规则

契约由因果关系子句组成,格式为:"A -> B"。 表示当输入满足 A 时,输出必然是 B。例如 "_ -> null" 表示方法对任何输入都返回 null。

输入约束支持以下类型:

  • _:任意值
  • null:空值
  • !null:非空值
  • true:布尔值 true
  • false:布尔值 false

输出约束除支持上述类型外,还支持:

  • fail:方法抛出异常
  • new:返回新对象(必须非空且不同于堆中已有对象)
  • this:返回调用对象本身(不适用于静态方法)
  • param1, param2:返回第1/2个参数的值

⚠️ newthisparam1 关键字仅支持 IntelliJ 2018.2+ 版本。多个约束可组合使用,但需确保不冲突。

3.2. 编写第一个契约

先看简单示例:创建 Person 类,添加链式调用方法:

public class Person {

    String name;

    @Contract("_ -> this")
    Person withName(String name) {
        this.name = name;
        return this;
    }
}

无论输入什么,方法都返回对象本身,因此使用 @Contract("_ -> this") 是合理的。

再看复杂契约:字符串拼接方法,当第二个参数为 null 时返回 null,否则返回拼接结果:

@Contract("_, null -> null; null, _ -> param2; _, !null -> !null")
String concatenateOnlyIfSecondArgumentIsNotNull(String head, String tail) {
    if (tail == null) {
        return null;
    }
    if (head == null) {
        return tail;
    }
    return head + tail;
}

契约解读:

  1. 第二参数为 null → 返回 null
  2. 第一参数为 null → 返回第二参数值
  3. 第二参数非空 → 返回非空结果

3.3. 代码检查效果

契约错误会导致两类问题:

  • 契约语法错误
  • 调用方出现不可达代码

通过 Code → Inspect Code 可触发检查:

inspect code intellij

示例1:错误契约

@Contract(" -> fail")
void doNothingWithWrongContract() {}

检查结果会提示契约违反: violated contract clause

示例2:冗余代码检测

String concatenation = concatenateOnlyIfSecondArgumentIsNotNull("1234", "5678");
if (concatenation != null) {  // 此条件永远为 true
    System.out.println(concatenation);
}

检查结果:

Condition 'concatenation != null' is always 'true'

✅ 契约让 IntelliJ 能智能识别冗余的空值检查。此外,IntelliJ 会自动为标准库方法推断契约,例如为 isEmpty() 添加 @Contract("null->true")

4. pure 属性详解

pure 属性声明方法无可见副作用。 当纯函数的返回值未被使用时,可安全移除调用。注意:

  • 控制台输出不算可见副作用
  • 可能暴露其他线程状态的方法不能标记为 pure

示例:字符串替换操作

@Contract(pure = true)
String replace(String string, char oldChar, char newChar) {
    return string.replace(oldChar, newChar);
}

可同时使用 valuepure

@Contract(value = "true -> false; false -> true", pure = true)
boolean not(boolean input) {
    return !input;
}

⚠️ 截至 IntelliJ 2023.1,pure 属性暂无对应代码检查功能,默认值为 false

6. 总结

本文系统介绍了 @Contract 注解的使用方法。将其加入工具箱能显著提升代码质量,特别是通过 value 属性快速发现死代码。但需注意:该注解仅用于静态分析,对编译结果无影响。

完整代码示例见 GitHub 仓库


原始标题:JetBrains @Contract Annotation