21

Just wondering what annotations could be used with variables declared in try-with-resources statements, which is allowed as per its grammar. The section 14.20.3 from the language specification (Java 7) reads,

TryWithResourcesStatement:
    try ResourceSpecification Block Catchesopt Finallyopt

ResourceSpecification:
    ( Resources ;opt )

Resources:
    Resource Resource ; Resources

Resource:
    VariableModifiersopt Type VariableDeclaratorId = Expression

And VariableModifiers expands as (section 14.4),

VariableModifiers:
    VariableModifier
    VariableModifiers VariableModifier

VariableModifier: one of
    Annotation final

There you go: VariableModifier can have Annotation. Well, that basically means, we can write something like this:

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

So my question is: how and what kind of annotations could possibly be used in try-with-resources and to achieve what kind of behaviors? Any innovative idea? Have anybody used them?

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • In addition to the examples given in the existing answers, don't forget that you can use reflection to give annotations any meaning you like. – Gene Aug 07 '17 at 02:39
  • @Gene that's for those that are available at runtime, which does not seem to be the case for [local variables](https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.4.2) : *An annotation on a local variable declaration is never retained in the binary representation.*. This seems to contradict [that](https://stackoverflow.com/a/41776190) answer by Michael Ernst (author of Checker Framework), but maybe he meant `RetentionPolicy.CLASS` which is useful for checkers but not available at runtime (except, maybe at best, by parsing class file yourself!) – Hugues M. Aug 07 '17 at 08:36
  • @Hugues M. Annotations with a target of `TYPE_USE` are stored within the class file and I suppose, Michael Ernst was thinking of these when making that statement, as you can see typical checker annotations like “non-null” and “nullable” as property of the type. Unfortunately, annotations with a target of `LOCAL_VARIABLE` are still not retained. – Holger Aug 07 '17 at 10:39
  • Ah yes thanks, I initially meant I don't see any way to use this with reflection (be it `LOCAL_VARIABLE` or `TYPE_USE`), but my details are incorrect, and I'll fix the corresponding (incorrect) part of my answer – Hugues M. Aug 07 '17 at 11:11
  • Thanks. I stand corrected. – Gene Aug 07 '17 at 23:54
  • 1
    sort of un-related but there is a test in jdk-9 sources that specifically tests the presence of a annotation on a resource used within try-wth-resouces – Eugene Aug 08 '17 at 21:44

2 Answers2

15

Not in Java 7, but I suspect you tagged this java-7 only because that's the version that introduced try-with-resources, and you are still interested in possible uses beyond Java 7 (I think this question is very interesting for Java >= 8).

I think there is nothing special that binds try-with-resources and annotations, it is not a special case in the grammar; in this respect such variables (declared within a try-with-resources statement) are just the same as other local variables, and the grammar allows annotations just as well:

  • Java 7 has introduced try-with-resources statements, in which you can declare a variable that will get special treatment.
  • The grammar has been allowing annotations on local variable declarations as early as Java 5, when annotations were introduced (but we had to wait Java 6 to get a useable API for annotation processing)
  • But even with Java 7 it was not possible for annotation processors to access annotations on local variables. The only annotation on local variable that was "useable" was @SuppressWarnings but that one was treated specially by the compiler itself, there was no way for you to hook into this.
  • Java 8 introduced a new kind of annotation context besides "declaration context", there is now "type context", and now an annotation Target can be ElementType.TYPE_USE

So the answer (with Java 8) is the same as with any annotation on local variables.


(some trivia about Java 8's new "type annotations")

... and this is where it becomes interesting: annotating any type use!

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.

Such annotations are not retained at runtime, but can be used at compile-time for a variety of "checks". See checker framework, which is built on top of work done for JSR-308 (by same author if I understand correctly).

Very quickly because it's fun, now we can do this:

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

Examples of such "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.

But none of those if specially useful in the context of a try-with-resources statement (I mean, no more or less than anywhere else).


Back to the question: are there uses for annotations on local variables that would be particularly interesting when declared within a try-with-resources statement?

I think in this case applications would essentially be limited to compile-time checks, because such an annotation will be either on the local variable, or on the type use, and neither is available at runtime (or not really):

So, I can think of one "special" use, but I'm not even sure that would be very useful as there are probably other ways to accomplish this: for some particular types of resources that you declare in a try-with-resources statement, you might need to make sure the resource is entirely consumed before it gets closed (I've seen something like that with an HTTP client library and the part of the API that reads headers -- can't remember the details).

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

This annotation would have target ElementType.LOCAL_VARIABLE (so would not require new Java 8 annotation types, but would require java 8 to be processeable), and the checker should probably verify that the variable is effectively declared within a try-with-resources statement (compiler cannot prevent from using it on any local variable), and then analyse the source tree to determine if the resources is consumed as required.
It would be probably impossible to implement such a checker in a 100% correct way, but on paper it looks possible to check for some known-bad patterns, and it would mostly make sense when the target variable is declared in a try-with-resources statement.

Another idea (still on variable and not type use), also very low usefulness: @MustNotEscape, if you want to control that the variable is not passed to another method because (for reasons similar to the above) you want the ability to control everything that happens to the object behind (e.g. as in previous idea), and that would be more difficult to accomplish if the variable is passed around.

To illustrate that such a thing is vaguely possible, here is an example of a framework that expects you to follow their "embedded DSL" inside a certain block, and fails if you don't. One could imagine an annotation to help check compliance with similar constraints imposed by an hypothetical framework on a resource within a try-with-resources block.
Not saying this would be a good design though... (I the case of ModelMapper, the DSL was only a clever trick they came up with before java 8, and they now have better & safer solutions with lambdas)

Hugues M.
  • 19,846
  • 6
  • 37
  • 65
7

The only kind of annotations you can apply to a local variable (including a variable assignment within a try of try-with-resources block) are the ones with that have @Target({ElementType.LOCAL_VARIABLE}). And those are not accessible at runtime (via reflection), so they can only be accessed by the compiler.

Examples when they could be useful:

  • @SuppressWarnings("unchecked") - if you have unchecked assignment within a try(...)
  • Using JSR 305 (Annotations for Software Defect Detection) annotations, such as @Nullable, @Nonnull
Sergey Khudyakov
  • 1,122
  • 1
  • 8
  • 15