在 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'必须是变量声明'[重复]