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, возможно, с помощью аннотаций.
Для класса почти невозможно гарантировать, что он всегда используется «правильно», но есть вещи, которые он может сделать, чтобы направить клиентов к правильному использованию (см.: 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
?
@Nullable/NotNull
, и это похоже по духу на то, что я хочу сделать, так что это должно быть выполнимо: jetbrains.com/idea/documentation/howto.html (IntelliJ IDEA предупреждает вас, если эти соглашения нарушаются.) - person polygenelubricants   schedule 01.09.2010InputStream.read
не идемпотент. На самом деле речь идет не об оптимизации компилятора, а о том, как написать удобный для пользователя API. - person polygenelubricants   schedule 01.09.2010InputStream.read(byte[])
не всегда заполняет буфер. Вы не должны отбрасывать возвращаемое значение, которое говорит вам, сколько байтов было фактически прочитано. - person polygenelubricants   schedule 01.09.2010