Аннотации к переменным, объявленным в try-with-resources?

Просто интересно, какие аннотации можно использовать с переменными, объявленными в операторах try-with-resources, что разрешено в соответствии с его грамматикой. Раздел 14.20.3 из спецификации языка (Java 7) гласит:

TryWithResourcesStatement:
try перехватывает блок ResourceSpecificationopt Наконецopt

Спецификация ресурса:
( Ресурсы ;выбрать )

Ресурсы:
Ресурс Ресурс ; Ресурсы

Ресурс:
VariableModifiersopt Type VariableDeclaratorId = Expression

А VariableModifiers раскрывается как (раздел 14.4),

VariableModifiers:
VariableModifier
VariableModifiers VariableModifier

VariableModifier: один из
аннотаций final

Вот и все: VariableModifier может иметь аннотацию. Ну, это в основном означает, что мы можем написать что-то вроде этого:

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

Итак, мой вопрос: как и какие аннотации можно использовать в try-with-resources и для достижения какого поведения? Любая инновационная идея? Кто-нибудь использовал их?


person Nawaz    schedule 02.08.2017    source источник
comment
В дополнение к примерам, приведенным в существующих ответах, не забывайте, что вы можете использовать отражение, чтобы придать аннотациям любое значение, которое вам нравится.   -  person Gene    schedule 07.08.2017
comment
@Gene для тех, которые доступны во время выполнения, что, похоже, не относится к локальные переменные : Аннотация объявления локальной переменной никогда не сохраняется в двоичном представлении.. Кажется, это противоречит этому ответу Майкла Эрнста (автора Checker Framework), но, возможно, он имел в виду RetentionPolicy.CLASS, который полезен для шашек, но не доступны во время выполнения (за исключением, может быть, в лучшем случае самостоятельного разбора файла класса!)   -  person Hugues M.    schedule 07.08.2017
comment
@Hugues M. Аннотации с целью TYPE_USE хранятся в файле класса, и я полагаю, Майкл Эрнст думал об этом, когда делал это утверждение, поскольку вы можете видеть типичные аннотации средства проверки, такие как «non-null» и «nullable» как свойство типа. К сожалению, аннотации с целью LOCAL_VARIABLE по-прежнему не сохраняются.   -  person Holger    schedule 07.08.2017
comment
Ах да, спасибо, я изначально имел в виду, что не вижу способа использовать это с отражением (будь то LOCAL_VARIABLE или TYPE_USE), но мои данные неверны, и я исправлю соответствующую (неправильную) часть моего ответа   -  person Hugues M.    schedule 07.08.2017
comment
Спасибо. Я исправляюсь.   -  person Gene    schedule 08.08.2017
comment
вроде не связанный, но в исходниках jdk-9 есть тест, который специально проверяет наличие аннотации к ресурсу, используемому в try-wth-resources   -  person Eugene    schedule 09.08.2017


Ответы (2)


Не в Java 7, но я подозреваю, что вы отметили этот java-7 только потому, что в этой версии введена попытка с ресурсами, и вас все еще интересуют возможные варианты использования за пределами Java 7 (я думаю, что этот вопрос очень интересен для Java >= 8) .

Я думаю, что нет ничего особенного, что связывает try-with-resources и аннотации, это не частный случай в грамматике; в этом отношении такие переменные (объявленные в операторе try-with-resources) точно такие же, как и другие локальные переменные, и грамматика также допускает аннотации:

  • В Java 7 появились операторы try-with-resources, в которых вы можете объявить переменную, которая получит особый режим.
  • Грамматика допускала аннотации к объявлениям локальных переменных еще в Java 5, когда аннотации были введены (но нам пришлось ждать Java 6, чтобы получить пригодный для использования API для обработки аннотаций).
  • Но даже с Java 7 процессоры аннотаций не могли получить доступ к аннотациям локальных переменных. Единственной аннотацией к локальной переменной, которая была «пригодна для использования», была @SuppressWarnings, но она была специально обработана самим компилятором, и у вас не было возможности подключиться к ней.
  • В Java 8 появился новый тип контекста аннотации, помимо «контекста объявления», теперь есть «контекст типа», и теперь аннотация Target может быть ElementType.TYPE_USE

Таким образом, ответ (с Java 8) такой же, как и для любой аннотации к локальным переменным.


(некоторые мелочи о новых «аннотациях типов» в Java 8)

... и вот здесь становится интересно: аннотирование любого типа!

Синтаксические области, в которых могут появляться аннотации, разделены на контексты объявлений , где аннотации применяются к объявлениям, и контексты типов, где аннотации применяются к типам, используемым в объявлениях и выражениях.

Такие аннотации не сохраняются во время выполнения, но могут использоваться во время компиляции для различных «проверок». См. среду проверки, созданную на основе работы, проделанной для 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 Framework предоставляет несколько аннотаций типов, которые могут быть полезны как разработчикам библиотек, так и разработчикам приложений, например:
@NonNull — компилятор может определить случаи, когда путь кода может получить нулевое значение, без необходимости отлаживать исключение NullPointerException.
@ReadOnly — компилятор будет помечать любую попытку изменить объект. Это похоже на Collections.unmodifiableList, но имеет более общий характер и проверяется во время компиляции.
@Regex — Обеспечивает проверку во время компиляции того, что строка, предназначенная для использования в качестве регулярного выражения, является правильно отформатированным регулярным выражением.
@Tainted и @Untainted – Идентификационные типы данных, которые не следует использовать вместе, например удаленный пользовательский ввод, используемый в системных командах, или конфиденциальная информация в потоках журналов.
@m – Единицы измерения обеспечивают использование и сравнение чисел, используемых для измерения объектов. правильно или подверглись надлежащему преобразованию единиц измерения.

Но ни один из них не особенно полезен в контексте оператора try-with-resources (я имею в виду, не больше и не меньше, чем где-либо еще).


Вернемся к вопросу: есть ли применение аннотациям к локальным переменным, которые были бы особенно интересны при объявлении в операторе 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. */
}

Эта аннотация будет иметь цель ElementType.LOCAL_VARIABLE (поэтому не потребуются новые типы аннотаций Java 8, но потребуется, чтобы Java 8 была обрабатываемой), и средство проверки, вероятно, должно проверить, что переменная эффективно объявлена ​​в операторе try-with-resources (компилятор не может запретить использовать его для какой-либо локальной переменной), а затем проанализировать исходное дерево, чтобы определить, расходуются ли ресурсы в соответствии с требованиями. выглядит возможным проверить некоторые известные плохие шаблоны, и в основном это имеет смысл, когда целевая переменная объявляется в операторе try-with-resources.

Другая идея (все еще об использовании переменной, а не типа), также очень низкая полезность: @MustNotEscape, если вы хотите контролировать, чтобы переменная не передавалась другому методу, потому что (по причинам, аналогичным вышеизложенным) вы хотите иметь возможность контролировать все, что происходит с объектом позади (например, как в предыдущей идее), и это было бы труднее выполнить, если бы переменная передавалась.

Чтобы проиллюстрировать, что такое смутно возможно, вот пример фреймворка который ожидает, что вы будете следовать их «встроенному DSL» внутри определенного блока, и не работает, если вы этого не сделаете. Можно представить себе аннотацию, помогающую проверить соответствие аналогичным ограничениям, наложенным гипотетической структурой на ресурс в блоке try-with-resources.
Не говорю, что это будет хороший дизайн... (Я В случае с ModelMapper DSL был всего лишь хитрым трюком, который они придумали до Java 8, и теперь у них есть лучшие и более безопасные решения с лямбда-выражениями)

person Hugues M.    schedule 06.08.2017

Единственный тип аннотаций, который вы можете применить к локальной переменной (включая назначение переменной в блоке try of try-with-resources), — это аннотации с @Target({ElementType.LOCAL_VARIABLE}). И они недоступны во время выполнения (через отражение), поэтому к ним может получить доступ только компилятор.

Примеры, когда они могут быть полезны:

  • @SuppressWarnings("unchecked") - если у вас есть непроверенное назначение в пределах try(...)
  • Использование аннотаций JSR 305 (Аннотации для обнаружения дефектов программного обеспечения), таких как @Nullable, @Nonnull
person Sergey Khudyakov    schedule 06.08.2017