在 try-with-resources 中声明的变量注释?

Annotations on variables declared in try-with-resources?

只是想知道哪些注释可以与在 try-with-resources 语句中声明的变量一起使用,这根据其语法是允许的。语言规范 (Java 7) 中的 14.20.3 部分显示,

TryWithResourcesStatement:
    try ResourceSpecification Block Catchesopt Finallyopt

ResourceSpecification:
    ( Resources ;opt )

Resources:
    Resource Resource ; Resources

Resource:
    VariableModifiersopt Type VariableDeclaratorId = Expression

VariableModifiers 扩展为 (section 14.4),

VariableModifiers:
    VariableModifier
    VariableModifiers VariableModifier

VariableModifier: one of
    Annotation final

好了:VariableModifier 可以有 Annotation。好吧,这基本上意味着,我们可以这样写:

try( @SomeAnnotation SomeType obj = createSomeType() ) { 
  //some code
}

所以我的问题是:在 try-with-resources 中可以使用什么样的注解以及如何使用注解并实现什么样的行为?有什么创新点子吗?有人用过吗?

唯一可以应用于局部变量的注释(包括 try of try-with-resources 块中的变量赋值)是具有 @Target({ElementType.LOCAL_VARIABLE}) 的注释。而且这些在运行时不可访问(通过反射),因此它们只能由编译器访问。

它们可能有用的示例:

  • @SuppressWarnings("unchecked") - 如果您在 try(...)
  • 中有未经检查的分配
  • 使用JSR 305(Annotations for Software Defect Detection)注解,如@Nullable@Nonnull

不在 Java 7 中,但我怀疑您标记此 java-7 只是因为这是引入 try-with-resources 的版本,并且您仍然对 [=120 以外的可能用途感兴趣=] 7(我觉得这个问题对Java >= 8很有意思)。

我认为绑定try-with-resources和注释没有什么特别的,它不是语法中的特例;在这方面,此类变量(在 try-with-resources 语句中声明)与其他局部变量相同,语法也允许注释:

  • Java 7 引入了try-with-resources语句,你可以在其中声明一个变量,它将得到special treatment.
  • 早在 Java 5 引入注解时,语法就允许 局部变量声明的注解(但我们不得不等待 Java 6 得到可用的 API 用于注释处理)
  • 但即使使用 Java 7,注解处理器也 not possible 可以访问局部变量上的注解。 "useable" 局部变量的唯一注解是 @SuppressWarnings,但编译器本身对其进行了特殊处理,您无法挂钩它。
  • Java 8 除了"declaration context",还引入了一种新的注解上下文,现在有"type context",现在还有注解Target 可以是 ElementType.TYPE_USE

所以答案(Java 8)与局部变量上的任何注释相同


(一些关于Java8的新"type annotations"的琐事)

...这就是它变得有趣的地方:注释任何类型使用!

The syntactic locations where annotations may appear are split into declaration contexts , where annotations apply to declarations, and type contexts, where annotations apply to types used in declarations and expressions.

此类注解不会在运行时保留,但可以在compile-time用于各种"checks"。如果我理解正确,请参阅 checker framework, which is built on top of work done for JSR-308 (by same author

很快,因为它很有趣,现在我们可以这样做了:

@NonNull Object @Nullable [] array; // Nullable array of non-null objects
@Nullable Object @NonNull [] array; // Non-null array of nullable objects

@Foo List<@Foo Integer> doSomething(@Foo Integer @Foo [] arrayOfIntegers, @Foo long x) {
    arrayOfIntegers[0] = (@Foo int) x;
    return Arrays.asList(arrayOfIntegers);
}

这样的例子"type annotations"

The Checker Framework provides a few Type Annotations that could benefit both library and application developers, such as:
@NonNull – The compiler can determine cases where a code path might receive a null value, without ever having to debug a NullPointerException.
@ReadOnly – The compiler will flag any attempt to change the object. This is similar to Collections.unmodifiableList, but more general and verified at compile time.
@Regex – Provides compile-time verification that a String intended to be used as a regular expression is a properly formatted regular expression.
@Tainted and @Untainted – Identity types of data that should not be used together, such as remote user input being used in system commands, or sensitive information in log streams.
@m – Units of measure ensures that numbers used for measuring objects are used and compared correctly, or have undergone the proper unit conversion.

但其中 none if 在 try-with-resources 语句的上下文中特别有用(我的意思是,不多于或少于其他任何地方)。


回到问题:在 try-with-resources 语句中声明时,局部变量的注释是否会特别有趣?

我认为在这种情况下,应用程序本质上会限于compile-time 检查,因为这样的注解要么在局部变量上,要么在类型使用上,并且在运行时都不可用(或实际上不可用):

所以,我可以想到一个 "special" 用途,但我什至不确定它是否非常有用,因为可能还有其他方法可以实现此目的:对于您声明的某些特定类型的资源在 try-with-resources 语句中,您可能需要确保资源在关闭之前完全消耗 (我在 HTTP 客户端库和 API 上面写着 headers -- 不记得细节了).

/* Say getResponse() taps into a third-party library that has a quirk:
 * a response object must be consumed entirely before being closed. */
try(@MustConsumeEntirely Stream<String> lines = getResponse()) {
    lines.findFirst().ifPresent(System.out::println);
    /* The stream is not entirely consumed (unless it is only 1 line but there is no way to tell).
     * A smart checker could catch this and issue a warning. */
}

此注释的目标为 ElementType.LOCAL_VARIABLE(因此不需要新的 Java 8 注释类型,但需要 java 8 才能处理),检查器可能会验证该变量在 try-with-resources 语句中有效声明(编译器无法阻止在任何局部变量上使用它),然后分析源代码树以确定是否按要求消耗了资源。
以 100% 正确的方式实现这样的检查器可能是不可能的,但在纸面上看起来可以检查一些 known-bad 模式,并且当目标变量在 try-with-resources声明。

另一种思路(还是关于变量而不是类型使用),用处也很低:@MustNotEscape,如果要控制变量不传给别的方法是因为(原因和上面类似) ) 您希望能够控制发生在 object 后面的所有事情(例如,如之前的想法),如果传递变量,这将更难实现。

为了说明这样的事情是有可能的,here is an example of a framework that expects you to follow their "embedded DSL" inside a certain block, and fails 如果你不这样做的话。人们可以想象一个注释来帮助检查是否符合假设框架对 try-with-resources 块中的资源施加的类似约束。
并不是说这将是一个好的设计...(我以 ModelMapper 为例,DSL 只是他们在 java 8 之前想出的一个聪明的技巧,他们现在有更好更安全的使用 lambda 的解决方案)