在 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 检查,因为这样的注解要么在局部变量上,要么在类型使用上,并且在运行时都不可用(或实际上不可用):
- according to the JLS 二进制表示中不保留局部变量的注释
- 关于类型使用的注释是 written to the class file, but still not available at runtime by reflection(您需要自己解析 class 文件!)
所以,我可以想到一个 "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 的解决方案)
只是想知道哪些注释可以与在 try-with-resources 语句中声明的变量一起使用,这根据其语法是允许的。语言规范 (Java 7) 中的 14.20.3 部分显示,
TryWithResourcesStatement:
try
ResourceSpecification Block Catchesopt FinallyoptResourceSpecification:
(
Resources;
opt)
Resources:
Resource Resource;
ResourcesResource:
VariableModifiersopt Type VariableDeclaratorId=
Expression
和 VariableModifiers 扩展为 (section 14.4),
VariableModifiers:
VariableModifier
VariableModifiers VariableModifierVariableModifier: one of
Annotationfinal
好了: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 检查,因为这样的注解要么在局部变量上,要么在类型使用上,并且在运行时都不可用(或实际上不可用):
- according to the JLS 二进制表示中不保留局部变量的注释
- 关于类型使用的注释是 written to the class file, but still not available at runtime by reflection(您需要自己解析 class 文件!)
所以,我可以想到一个 "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 的解决方案)