Подтвердить что ты не робот

Использование аннотации для обеспечения того, чтобы возвращаемое методом значение не отбрасывалось

String в Java неизменен. Следующий фрагмент, вообще говоря, "неправильный".

String s = "hello world!";

s.toUpperCase(); // "wrong"!!

System.out.println(s); // still "hello world!"!!!

Несмотря на то, что это "неправильно", код компилируется и запускается, возможно, в замешательство многих новичков, которым нужно либо сказать, что это за ошибка, либо узнать сами, обратившись к документации.

Чтение документации является неотъемлемой частью понимания API, но мне интересно, можно ли это дополнять дополнительными проверками времени компиляции. В частности, мне интересно, можно ли использовать Java-аннотацию для обеспечения того, чтобы значение, возвращаемое некоторыми методами, не игнорировалось. Дизайнеры API/авторы библиотек затем использовали бы эту аннотацию в своих методах для документирования того, какие возвращаемые значения не следует игнорировать.

Как только API будет дополнен этой аннотацией (или, возможно, другим механизмом), тогда всякий раз, когда пользователь пишет код, такой как выше, он не будет компилироваться (или делать это с кормовым предупреждением).

Так можно ли это сделать, и как бы вы сделали что-то подобное?


Приложение: Мотивация

Кажется очевидным, что в общем случае Java должна позволять игнорировать возвращаемые значения методов. Возвращаемые значения методов, таких как List.add (всегда true), System.setProperty (предыдущее значение), можно, вероятно, безопасно игнорировать большую часть времени.

Однако есть также много методов, возвращаемые значения которых НЕ должны игнорироваться. Это почти всегда является ошибкой программиста или, в противном случае, неправильным использованием API. К ним относятся такие вещи, как:

  • Способы неизменяемых типов (например, String, BigInteger и т.д.), которые возвращают результат операций вместо того, чтобы мутировать экземпляр, на который он вызывается.
  • Методы, возвращающее значение которых является критичной частью его поведения и не должно игнорироваться, но люди иногда делают это (например, InputStream.read(byte[]) возвращает количество прочитанных байтов, которое НЕ следует считать всей длиной массива)

В настоящее время мы можем писать коды, которые игнорируют эти возвращаемые значения и компилируются и запускаются без предупреждения. Статические аналитические проверки/искатели ошибок/стилисты/и т.д. Могут почти наверняка отмечать их как возможные запахи кода, но, похоже, это будет подходящим/идеальным, если это может быть реализовано самим API, возможно, через аннотации.

Для класса почти невозможно гарантировать, что он всегда используется "правильно", но есть вещи, которые он может сделать, чтобы помочь правильному использованию клиентов (см.: Эффективное Java 2nd Edition, пункт 58: Использовать проверенные исключения для восстанавливаемые условия и исключения во время выполнения для ошибок программирования и пункт 62: Документировать все исключения, выданные каждым методом). Наличие аннотации, которая заставила бы клиентов не игнорировать возвращаемые значения определенных методов и принудительное выполнение компилятором во время компиляции в виде ошибок или предупреждений, похоже, соответствует этой идее.


Приложение 2: фрагмент

Ниже приведена предварительная попытка, которая кратко иллюстрирует то, что я хочу достичь:

@interface Undiscardable { }
//attachable to methods to indicate that its
//return value must not be discarded

public class UndiscardableTest {
     public static @Undiscardable int f() {
             return 42;
     }

     public static void main(String[] args) {
             f(); // what do I have to do so this generates
                  // compilation warning/error?

             System.out.println(f()); // this one would be fine!
     }
}

Вышеприведенный код компилируется и работает отлично (как видно на ideone.com). Как я могу сделать это не так? Как назначить семантику, которую я хочу использовать @Undiscardable?

4b9b3361

Ответ 1

Я не уверен в возможности, особенно в переносном смысле, но смотрю на Roman Numerals, на нашей Java (код GitHub) из Адриан Кун. Он использовал И Sun javac частный API для добавления римских нумерованных литералов к Java, посетив исходный код для замены.

Возможно, вы можете использовать аналогичный подход:

  • найти вызовы в ваш аннотированный метод в исходном коде
  • проверить, назначен ли результат (будет непросто ИМО)
    • генерировать предупреждение компилятора, если не

И не пропустите следующие ресурсы из сообщения Adrian:

Вам также может понравиться

Ссылка

Связанные вопросы

Ответ 2

Вы также можете проверить jsr305, он определяет аннотацию @CheckReturnValue. Он совместим с findbugs и генерирует предупреждение, когда кто-то забывает обрабатывать возвращаемое значение.

Guavas Splitter использует его: http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Splitter.java

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

Ответ 3

В орехе: вы хотели бы иметь @Deprecated как аннотация, которая помогла бы компилятору/среде IDE для предупреждения/ошибки когда метод вызывается без присвоения его результата? Вы не можете достичь этого, не изменяя исходный код Java и компилятор. Конкретный метод должен быть аннотирован, и компилятор должен знать об этом. Без изменения источника и/или компилятора вы можете на самом высоком уровне создать плагин/параметр IDE, который распознает случаи и соответственно генерирует ошибку/предупреждение.


Обновить: вы можете написать фреймворк/плагин вокруг него, который соответствующим образом проверяет вызванный метод и ошибки. Вам просто хотелось бы, чтобы аннотация была доступна во время выполнения. Вы можете сделать это, аннотируя аннотацию, используя @Retention (RetentionPolicy.RUNTIME). Затем вы можете использовать Method#getAnnotation(), чтобы определить, доступна ли эта аннотация. Вот пример запуска, как такая структура могла бы выполнить эту работу:

package com.example;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class Test {

    public static void main(String[] args) throws Exception {
        if (Test.class.getMethod("f", new Class[0]).getAnnotation(Undiscardable.class) != null) {
            System.err.println("You should not discard the return value of f()!");
        } else {
            f();
        }

        System.out.println(f());
    }

    public static @Undiscardable int f() {
        return 42;
    }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Undiscardable {}

Тем не менее, чтобы заставить компилятор выполнить задание вместо этого, вам нужно сделать немного больше работы.

Ответ 4

Вам не нужно указывать аннотацию. Вы можете определить правило при вызове метода:

  • метод имеет тип возврата void;
  • результат метода используется как аргумент для другого вызова метода; или
  • результат метода присваивается переменной.

Вы можете реализовать Процессор, который применяет это правило или реализует Checkstyle, который применяет это правило.

Ответ 5

Отказ от ответственности: На самом деле, у меня есть тот же вопрос и еще не полное решение. НО:

У меня есть идея, как это можно сделать чистым способом, который я хочу публиковать здесь, пытаясь сделать это:

  • Можно использовать AspectJ для вызова кода после вызова определенного метода. Например,

     @AfterReturning (pointcut = "call (int Foo.m(int))", return = "x" )
    public void doSomething (int x) {...}
    может быть использован. Возвращаемое значение x передается вашему методу.
  • Затем ваш метод может наблюдать за количеством ссылок возвращаемого значения. Если возвращаемое значение Garbadge Collected было выброшено, и вы могли бы выдать предупреждение, см., Например, http://java.dzone.com/articles/letting-garbage-collector-do-c

Конечно, я бы предпочел аннотацию и поддержку времени компиляции, потому что выше, возможно, подходит только в тестовой среде и, возможно, не в производстве (из-за ее влияния на производительность).

Любые комментарии, если это может сработать?

Ответ 6

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

Пока это кажется хорошей идеей, я не думаю, что это так. Хотим ли мы загромождать код уведомлениями пользователей о своей плохой практике? Есть много продуктов, которые смотрят на код и сообщают вам, когда вы делаете что-то неправильное (или нежелательное), например, Lint, Sonar и даже JavaDocs в меньшей степени.

Что, если вы не согласны с тем, что сказал писатель библиотеки, теперь мы ожидаем использовать @SuppressWarnings ( "return-discarded" ).

Хотя это может быть полезно в качестве учебного пособия, мой вопрос больше связан с разделением проблем, чем с помощью начинающих программистов. Код (и аннотации) в классе должен быть связан с функцией класса и не указывать политику того, когда и как использовать его методы.