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

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

TryWithResourcesStatement:
try ResourceSpecification Block Catchesopt 最後選擇

资源规格:
资源 ;选择

资源:
资源资源 ;资源

资源:
变量修改器opt 类型 变量声明器 Id = 表达式

变量修改器扩展为(第14.4节),

变量修改器:
变量修改器
变量修改器 变量修改器

变量修改器:注释最终版本之

你去吧:VariableModifier可以有Notestin。好吧,这基本上意味着,我们可以写这样的东西:

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

所以我的问题是:如何以及什么样的注释可以用于尝试资源并实现什么样的行为?有什么创新的想法吗?有人用过它们吗?


答案 1

不是在Java 7中,但我怀疑你标记这个只是因为这是引入try-with-resources的版本,并且你仍然对Java 7以外的可能用途感兴趣(我认为这个问题对于Java>= 8来说非常有趣)。java-7

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

  • Java 7 引入了 try-with-resources 语句,您可以在其中声明一个将获得特殊处理的变量。
  • 早在Java 5引入注释时,语法就允许对局部变量声明进行注释(但是我们不得不等待Java 6才能获得用于注释处理的可用API)。
  • 但即使使用Java 7,注释处理器也不可能访问局部变量上的注释。关于局部变量的唯一“可用”的注释是,但是编译器本身专门处理了该注释,因此您无法将其挂钩。@SuppressWarnings
  • Java 8除了“声明上下文”之外,还引入了一种新的注解上下文,现在有“类型上下文”,现在注解可以TargetElementType.TYPE_USE

因此,答案(使用Java 8)与局部变量上的任何注释相同


(关于Java 8的新“类型注释”的一些琐事)

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

可能出现批注的语法位置被拆分为声明上下文 ,其中批注适用于声明,以及类型上下文,其中批注适用于声明和表达式中使用的类型。

此类注释不会在运行时保留,但可以在编译时用于各种“检查”。请参阅checker框架,它是在为JSR-308所做的工作之上构建的(如果我理解正确,由同一作者完成)。

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

@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);
}

此类“类型注释”的示例

Checker 框架提供了一些类型注释,这些注释可能会使库和应用程序开发人员都受益,例如:
- 编译器可以确定代码路径可能收到空值的情况,而无需调试 NullPointerException。
– 编译器将标记任何更改对象的尝试。这类似于 Collections.unmodifiableList,但更通用,并在编译时进行验证。
– 提供编译时验证,确保用作正则表达式的 String 是否为格式正确的正则表达式。
and – 标识不应一起使用的数据类型,例如系统命令中使用的远程用户输入或日志流中的敏感信息。
– 测量单位确保用于测量对象的数字被正确使用和比较,或者经过适当的单位转换。@NonNull@ReadOnly@Regex@Tainted@Untainted@m

但是,在尝试使用资源声明的上下文中,这些都不是特别有用的(我的意思是,不比其他任何地方多或少)。


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

我认为在这种情况下,应用程序基本上仅限于编译时检查,因为这样的注释要么在局部变量上,要么在类型使用上,并且在运行时都不可用(或者实际上不可用):

所以,我可以想到一个“特殊”的用法,但我甚至不确定这会非常有用,因为可能还有其他方法可以实现这一点:对于您在try-with-resources语句中声明的某些特定类型的资源,您可能需要确保资源在关闭之前完全被消耗(我已经在HTTP客户端库和读取标头的API部分中看到过类似的东西 - 不能记住细节)。

/* 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. */
}

此注释将具有目标(因此不需要新的 Java 8 注释类型,但需要 java 8 是可处理的),并且检查器可能应该验证该变量是否在 try-with-resources 语句中有效地声明(编译器不能阻止在任何局部变量上使用它),然后分析源代码树以确定资源是否按要求使用。
以100%正确的方式实现这样的检查器可能是不可能的,但是在纸面上,似乎可以检查一些已知的不良模式,并且当在try-with-resources语句中声明目标变量时,它基本上是有意义的。ElementType.LOCAL_VARIABLE

另一个想法(仍然在变量上而不是类型使用),也非常低的有用性:,如果你想控制变量不传递给另一个方法,因为(出于与上述类似的原因)你希望能够控制发生在后面的对象上的所有事情(例如,如以前的想法),如果变量被传递,这将更加困难。@MustNotEscape

为了说明这样的事情是模糊可能的,这里有一个框架的例子,它期望你在某个块内遵循他们的“嵌入式DSL”,如果你不这样做,就会失败。人们可以想象一个注释来帮助检查对假设框架对资源试用块中的资源施加的类似约束的合规性。
虽然不是说这将是一个很好的设计...(以ModelMapper为例,DSL只是他们在java 8之前想出的一个聪明的技巧,他们现在有更好,更安全的lambda解决方案)


答案 2

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

当它们可能有用时,示例:

  • @SuppressWarnings("unchecked")- 如果您在try(...)
  • 使用 JSR 305(软件缺陷检测的注释)注释,例如 、@Nullable@Nonnull

推荐