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

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 (предыдущее значение), вероятно, можно игнорировать большую частьtimes.

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

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

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

Для класса почти невозможно гарантировать, что он всегда используется «должным образом», но есть вещи, которые он может сделать, чтобы помочь клиентам правильно их использовать (см .: Effective Java 2nd Edition, Item 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?

Ответы [ 7 ]

8 голосов
/ 01 сентября 2010

Я не уверен в целесообразности - особенно в переносном виде - но взгляните на римские цифры в нашем Java ( код GitHub ) от Адриан Кун . Он использовал обработку аннотаций AND Закрытый API Sun javac для добавления литералов римских цифр в Java , посетив исходный код, чтобы выполнить некоторую замену .

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

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

И не пропустите следующие ресурсы из поста Адриана:

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

* * Ссылка тысяча сорок-девять

Похожие вопросы

7 голосов
/ 08 марта 2012

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

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

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

2 голосов
/ 24 сентября 2018

На Android вы можете использовать @CheckResult для отображения предупреждения, если возвращаемое значение не используется.

public class ImmutableObject {

    public final int value;

    public ImmutableObject(int value) {
        this.value = value;
    }

    @CheckResult
    public ImmutableObject addOne() {
        return new ImmutableObject(value + 1);
    }
}

Это выдаст предупреждение:

ImmutableObject obj = new ImmutableObj();
obj.addOne();  // Warning here
ImmutableObject obj2 = obj.addOne();  // No warning

Если вы используете RxJava, вы также можете использовать @CheckReturnValue.

2 голосов
/ 01 сентября 2010

В орехе: вы хотели бы иметь аннотацию типа @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 {}

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

1 голос
/ 30 июня 2013

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

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

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

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

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

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

1 голос
/ 01 сентября 2010

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

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

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

0 голосов
/ 26 сентября 2016

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

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

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

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

...