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

Posted

技术标签:

【中文标题】在 try-with-resources 中声明的变量的注释?【英文标题】:Annotations on variables declared in try-with-resources? 【发布时间】:2017-08-02 15:24:10 【问题描述】:

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

TryWithResources 声明:try ResourceSpecification 块捕获opt 最后opt

资源规范:( 资源 ;opt)

资源: 资源资源;资源

资源:VariableModifiersopt 类型 VariableDeclaratorId = 表达式

VariableModifiers 扩展为(14.4 节),

变量修饰符: 变量修饰符 变量修饰符变量修饰符

VariableModifier:其中之一注释 final

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

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

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

【问题讨论】:

除了现有答案中给出的例子,别忘了你可以使用反射给注释赋予任何你喜欢的意义。 @Gene 用于在运行时可用的那些,而local variables 似乎并非如此:局部变量声明上的注释永远不会保留在二进制表示中。 。这似乎与 Michael Ernst(Checker Framework 的作者)的 that 回答相矛盾,但也许他的意思是 RetentionPolicy.CLASS 这对检查器很有用,但在运行时不可用(除了,也许充其量,通过自己解析类文件!) @Hugues M. 以TYPE_USE 为目标的注释存储在类文件中,我想,Michael Ernst 在做出该声明时正在考虑这些,因为您可以看到典型的检查器注释,例如“非空”和“可空”作为类型的属性。不幸的是,目标为LOCAL_VARIABLE 的注释仍未保留。 啊,是的,谢谢,我最初的意思是我看不到任何方法可以通过反射来使用它(无论是 LOCAL_VARIABLE 还是 TYPE_USE),但我的详细信息不正确,我会修复我答案的相应(不正确)部分 有点不相关,但在 jdk-9 源中有一个测试,专门测试在 try-wth-resouces 中使用的资源上是否存在注释 【参考方案1】:

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

我认为绑定try-with-resources和annotation没有什么特别的,在语法上不是特例;在这方面,这些变量(在 try-with-resources 语句中声明)与其他局部变量一样,并且语法也允许注释:

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

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


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

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

注释可能出现的句法位置被分成声明上下文,其中注释适用于声明, 和类型上下文,其中注释适用于声明和表达式中使用的类型。

此类注解不会在运行时保留,但可以在编译时用于各种“检查”。请参阅checker framework,它建立在为JSR-308 完成的工作之上(如果我理解正确,则由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"的例子:

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

但是,如果在 try-with-resources 语句的上下文中特别有用的话,这些都没有(我的意思是,不比其他任何地方多或少)。


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

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

according to the JLS 局部变量的注释不会保留在二进制表示中 类型使用的注释是written to the class file,但still not available at runtime by reflection(你需要自己解析类文件!)

所以,我可以想到一种“特殊”用途,但我什至不确定这是否非常有用,因为可能还有其他方法可以实现这一点:对于您在 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. */

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

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

为了说明这样的事情是隐约可能的,here is an example 一个框架希望你在某个块内遵循他们的“嵌入式 DSL”,如果你不这样做,fails。可以想象一个注释来帮助检查假设框架对 try-with-resources 块中的资源施加的类似约束的合规性。虽然不是说这将是一个好的设计......(我以 ModelMapper 为例,DSL 只是他们在 java 8 之前提出的一个聪明的技巧,现在他们有了更好、更安全的 lambda 解决方案)

【讨论】:

【参考方案2】:

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

可能有用的示例:

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

【讨论】:

以上是关于在 try-with-resources 中声明的变量的注释?的主要内容,如果未能解决你的问题,请参考以下文章

[Java开发之路](20)try-with-resource 异常声明

Try-with-resource'必须是变量声明'[重复]

try-with-resources语句

Jdk9新特性增强try-with-resource

java 7新特性-TWR(Try-with-resources)

谁在 close 方法中捕获异常?(try-with-resources)