看看这个简单的代码。
\n\ntry (@Foo Stream<@Bar Baz> foo = blabla) { }\nRun Code Online (Sandbox Code Playgroud)\n\n我们知道这@Bar是注释Baz,并且@Foo是注释Stream(我在这里写了一个类似的例子,在线编译它!)。
但是这段代码呢?
\n\nvoid whatever(@Foo String[] args) { }\nRun Code Online (Sandbox Code Playgroud)\n\n这里我们有一个String[]注释@Foo(无论注释是什么,对于这个问题来说并不重要)。
我的问题是,注释@Foo在String或 上String[]?
确定注释的目标非常重要,因为有时我们使用注释来@NotNull表示类型的可为空性,并且@NotNull List<String>意味着永远不为空的列表包含一些可能为空的字符串;List<@NotNull String>表示一个可能为空但成员永远不为空的列表。
一个可能的用例:我需要一个@NotNull来显示args不为空,另一个@NotNull来显示 的成员args也不为空?我需要同时注释它们。如果args是a java.util.List,我可以使用@NotNull List<@NotNull String>。Butargs是一个数组——我不知道注释如何影响args.
在数组级别之前定义@NonNull似乎可以解决问题(至少对于 Checker 框架而言):
import org.checkerframework.checker.nullness.qual.NonNull;\n\nclass App {\n void foo() {\n String @NonNull [] bar;\n bar = null; // NOK\n bar = new String[1];\n bar[0] = null; // NOK\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n导致两个错误(参见现场演示):
\n\n| No. | Type | Description | Line | Column |\n|-----|-------|-----------------------------------------------------------------------------|------|--------|\n| 1 | error | Error: [assignment.type.incompatible] incompatible types in assignment. | 6 | 15 |\n| | | found : null | | |\n| | | required: @Initialized @NonNull String @UnknownInitialization @NonNull [] | | |\n| 2 | error | Error: [assignment.type.incompatible] incompatible types in assignment. | 8 | 18 |\n| | | found : null | | |\n| | | required: @Initialized @NonNull String | | |\nRun Code Online (Sandbox Code Playgroud)\n\n要回答您的实际问题,请参阅规范 \xc2\xa79.7.4:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n\n@C int @A [] @B [] f;\n\n
@A适用于数组类型int[][]、@B适用于其组件类型\nint[]以及@C适用于元素类型int。
实际上@Foo String[] args是注释String(读作:非空字符串的可能为空数组)。
@Foo String[] args对于“什么是注释?”这个问题有两种可能的答案@Foo。
如果@Foo是类型注释(即 的定义Foo是用 进行元注释的@Target(ElementType.TYPE_USE)),则@Foo适用于元素类型String,并且您已经声明了 的数组@Foo String。
如果Foo是声明注释(其定义未使用 元注释@Target(ElementType.TYPE_USE)),则@Foo适用于整个声明String[] args。这对于形式参数来说并不常见。
在该位置,@Foo注释不能引用数组类型String[]。
(另一个答案给出了特别有用的答案@NonNull,但没有回答原来的问题。)