JUnit 6 Nullability: @Nullable, @NotNull & @NullMarked

随着 JUnit 6.0.0 的发布,维护者在所有 JUnit 模块中采用了 JSpecify nullability 注解。这意味着 JUnit 中的方法参数、返回类型和其他 API 元素现在都带有注解,以明确声明它们是否接受或返回 null。

JUnit6 Logo

随着 JUnit 6.0.0 的发布,维护者在所有 JUnit 模块中采用了 JSpecify 空值性注解。这意味着 JUnit 中的方法参数、返回类型和其他 API 元素现在都已标注,以明确声明它们是否接受或返回 null

因此,测试(或扩展)更安全,并且您可以在编译时或 IDE 时获得提示,而无需仅依赖运行时行为。

如果我们将一个参数标注为 @NonNull,并且在运行时意外传递 null程序仍然会运行,并且可能只有在我们尝试使用该 null 值时才会抛出 NullPointerException

该注解本身并不会将任何空值检查插入到方法中

1. 与空值相关的注解 (通过 JUnit 6 中的 JSpecify)

我们可以使用以下注解来指定测试的null相关行为

注解描述 / 用途
@Nullable– 这会将用法类型(参数类型、返回类型、字段类型等)标记为nullable
– 此值可能为 null
– 这会告知 IDE/工具/静态分析,并帮助用户了解空值安全契约。
@NonNull– 这会将用法类型标记为非 null
– 此值一定不能为 null
– 这可确保调用者/实现将它视为非 null,从而提供更强的保证。
@NullMarked– 这是一个包/类/模块级别的注解。
– 这表示在注释范围内,未注释的类型默认应被视为非空(除非显式标记为 @Nullable)。
– 这有助于减少冗余,即不必标记每种类型为非空,而只需标记可为空的类型。

使用 @NullMarked 可以减少注释的繁琐。与其显式标记每种非空类型,不如可以注释整个类/包/模块,然后仅标记那些可为空的类型。

2. JUnit 6 @Nullable 示例

在 JUnit 6 中,@Nullable 来自 JUnit 6 采用的 JSpecify 注解,用于指示参数或返回值是否可以为 null。 这不会改变测试执行,但它有助于 IDE 检查、静态分析和 Kotlin 互操作。

这是一个简单的示例,展示了测试中的 @Nullable

import org.junit.jupiter.api.Test;
import org.jspecify.annotations.Nullable;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

class NullableExampleTest {

    // A simple method that may return null
    @Nullable
    String getNullableString(boolean returnNull) {
        if (returnNull) {
            return null;
        } else {
            return "Hello, JUnit 6!";
        }
    }

    @Test
    void testNullableReturn() {
        // Test case 1: method returns a string
        String result1 = getNullableString(false);
        assertEquals("Hello, JUnit 6!", result1);

        // Test case 2: method returns null
        String result2 = getNullableString(true);
        assertNull(result2); // @Nullable tells the IDE/static analysis that this can be null
    }

    @Test
    void testNullableParameter() {
        // @Nullable can also be used on parameters
        printMessage(null);
        printMessage("JUnit 6 is great!");
    }

    void printMessage(@Nullable String message) {
        if (message == null) {
            System.out.println("No message provided");
        } else {
            System.out.println(message);
        }
    }
}

程序输出

No message provided
JUnit 6 is great!

3. JUnit 6 @NonNull 示例

在 JUnit 6 中,@NonNull 表示一个方法不应返回 null,或者一个参数不应为 null。 IDE、静态分析工具和 Kotlin 互操作使用此元数据来捕获潜在的空安全违规,在编译时

这是一个简单的 JUnit 6 示例

import org.junit.jupiter.api.Test;
import org.jspecify.annotations.NonNull;

import static org.junit.jupiter.api.Assertions.assertEquals;

class NonNullExampleTest {

    // Method guarantees a non-null return value
    @NonNull
    String getNonNullMessage(boolean condition) {
        if (condition) {
            return "JUnit 6 rocks!";
        } else {
            return "Null is not allowed!"; // must return non-null
        }
    }

    @Test
    void testNonNullReturn() {
        String result = getNonNullMessage(true);
        assertEquals("JUnit 6 rocks!", result);

        String result2 = getNonNullMessage(false);
        assertEquals("Null is not allowed!", result2);
    }

    @Test
    void testNonNullParameter() {
        printMessage("Hello, JUnit 6!");
        // printMessage(null); // IDE/static analyzer will warn: null not allowed
    }

    // Parameter marked @NonNull → must not be null
    void printMessage(@NonNull String message) {
        // No need to check for null; tools assume non-null
        System.out.println(message.toUpperCase());
    }
}

程序输出

HELLO, JUNIT 6!

4. JUnit 6 @NullMarked 示例

在 JUnit 6 中,@NullMarked 是一个包级、类级或模块级注解,表示该范围内的所有未注解的类型默认情况下都为非空。 您只需要使用 @Nullable 注解可以为null 的值。

这是一个使用 @NullMarked 的 JUnit 6 示例

import org.jspecify.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullMarked;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

@NullMarked  // All unannotated types in this class are treated as @NonNull
class NullMarkedExampleTest {

    // Return type is non-null by default because of @NullMarked
    String getMessage(boolean condition) {
        return condition ? "JUnit 6 is awesome!" : "Null is not allowed!";
    }

    // Parameter is non-null by default
    void printMessage(String message) {
        System.out.println(message.toUpperCase());
    }

    // Only mark explicitly nullable parameters/returns
    @Nullable
    String getNullableMessage(boolean condition) {
        return condition ? "Some message" : null; // nullable allowed
    }

    @Test
    void testNonNullBehavior() {
        String msg = getMessage(true);
        assertEquals("JUnit 6 is awesome!", msg);

        // IDE or static analysis will warn if you try to pass null here:
        // printMessage(null); // ❌ warning
        printMessage(msg); // ✅ OK
    }

    @Test
    void testNullableBehavior() {
        String maybeNull = getNullableMessage(false);
        assertNull(maybeNull); // ✅ allowed because @Nullable
    }
}

程序输出

JUNIT 6 IS AWESOME!

5. 结论

JUnit 6 采用的 JSpecify 空值注解 @Nullable@NonNull@NullMarked 代表了类型安全和开发人员指导方面的一大改进。 虽然这些注解不会强制执行运行时空值检查,但它们显著提高了代码安全性、可维护性和可读性,帮助开发人员编写更可靠和更具表现力的测试。

祝您学习愉快!!

评论

订阅
通知
0 条评论
最多投票
最新 最旧
内联反馈
查看所有评论

关于我们

HowToDoInJava 提供 Java 和相关技术的教程和操作指南。

它还分享最佳实践、算法和解决方案以及经常被问到的面试题。

我们的博客

REST API 教程

关注我们