Поддерживать API сложно.
Мы поддерживаем API jOOQ, который чрезвычайно сложен. Но мы следуем относительно смягченным правилам в том, что касается семантического управления версиями .
Когда вы читаете комментарии Брайана Гетца и других о том, как поддерживать обратную совместимость в JDK, я могу только выразить большое уважение к их работе. Очевидно, что мы все хотим, чтобы такие вещи, как Vector
, Stack
, Hashtable
были окончательно удалены, но есть некоторые крайние случаи, связанные с обратной совместимостью, вокруг API коллекций, о которых обычные смертные никогда не подумают. Например: почему Java Collections не удаляют общие методы?
Лучше амортизация
С Java 9, Jigsaw и модульностью, одна из основных движущих целей новых функций заключается в том, чтобы иметь возможность «отрезать» части JDK, а также аккуратно отказаться от них и удалить их в следующих выпусках. И как часть этого усовершенствования, Стюарт Маркс, доктор медицины, доктор юридических наук , предложил JEP 277: «Улучшенный износ» .
Идея состоит в том, чтобы улучшить аннотацию @Deprecated
с помощью некоторой дополнительной информации, такой как:
- НЕУТОЧНЕННЫЙ . Этот API устарел без объяснения причин. Это значение по умолчанию; все, что сегодня осуждается неявно, имеет причину непризнания UNSPECIFIED.
- ОСУЖДЕН . Этот API предназначен для удаления в будущем выпуске JDK. Обратите внимание, что использование слова «осужденный» здесь используется в смысле структуры, которая предназначена для разрушения. Термин не подразумевает никакого морального осуждения.
- ОПАСНО . Использование этого API может привести к потере данных, взаимоблокировке, уязвимости безопасности, неверным результатам или потере целостности JVM.
- ОБОСЛОВЛЕНИЕ . Этот API больше не нужен, и использование должно быть удалено. API замены не существует. Обратите внимание, что API-интерфейсы OBSOLETE могут или не могут быть помечены как ОСУЩЕСТВЛЕННЫЕ.
- СУПЕРСЕДЕН . Этот API был заменен более новым API, и его использование следует перенести из этого API в более новый API. Обратите внимание, что SUPERSEDED API могут или не могут быть помечены как CONDEMNED.
- НЕОБХОДИМЫЙ . Вызов этого не имеет никакого эффекта или безоговорочно вызовет исключение.
- ЭКСПЕРИМЕНТАЛЬНАЯ . Этот API не является стабильной частью спецификации, и он может измениться несовместимо или исчезнуть в любое время.
При отклонении материала важно уметь сообщать о намерениях устаревания. Этого также можно достичь с помощью тега @deprecated
Javadoc, где можно генерировать любой вид текста.
Альтернативное, намного лучшее решение
Вышеупомянутое предложение страдает от следующих проблем:
- Это не растяжимо . Вышесказанного может быть достаточно для разработчиков библиотек JDK, но мы, как сторонние поставщики API, хотим иметь в перечислении гораздо больше элементов, кроме CONDEMNED, DANGEROUS и т. Д.
- Все еще нет простой текстовой информации . Между этой аннотацией и тегом Javadoc все еще существует избыточность, поскольку мы все еще не можем формально предоставить текст аннотации, который проясняет, например, мотивация того, почему что-то «ОПАСНО».
- «Устаревший» — это неправильно . Идея пометить что-то НЕОБХОДИМЫЕ или ЭКСПЕРИМЕНТАЛЬНЫЕ как «устаревшие» показывает обходной характер этого JEP, который пытается включить некоторые новые функции в существующие имена.
У меня такое ощущение, что JEP слишком боится трогать слишком много частей. Тем не менее, была бы чрезвычайно простая альтернатива, которая намного лучше для всех:
1
2
3
4
|
public @interface Warning { String name() default "warning" ; String description() default "" ; } |
Нет необходимости ограничивать количество возможных типов предупреждений ограниченным списком констант. Вместо этого у нас может быть аннотация @Warning
которая принимает любую строку!
Конечно, JDK может иметь набор хорошо известных строковых значений, таких как:
1
2
3
4
5
6
7
|
public interface ResultSet { @Deprecated @Warning (name= "OBSOLETE" ) InputStream getUnicodeStream( int columnIndex); } |
или же…
1
2
3
4
5
|
public interface Collection<E> { @Warning (name= "OPTIONAL" ) boolean remove(Object o); } |
Обратите внимание, что хотя ResultSet.getUnicodeStream()
JDBC действительно не рекомендуется в смысле «OBSOLETE», мы также можем добавить подсказку к методу Collection.remove()
, который применяется только к типу Collection
, а не ко многим его подтипам. ,
Интересно, что при таком подходе мы также можем улучшить полезную аннотацию @SuppressWarnings
, потому что иногда мы просто знаем KnowWhatWeAreDoing ™, например, при написании таких вещей, как:
1
2
3
4
5
|
Collection<Integer> collection = new ArrayList<>(); // Compiler!! Stop bitching @SuppressWarnings ( "OPTIONAL" ) boolean ok = collection.remove( 1 ); |
Такой подход решит много проблем за один раз:
- У сопровождающих JDK есть то, что они хотят. Хороший инструмент для мягкого устаревания JDK
- Не очень хорошо документированный беспорядок вокруг того, что можно сделать с
@SuppressWarnings
конечном итоге был бы немного более чистым и формальным - Мы могли бы выдавать тонны пользовательских предупреждений нашим пользователям, в зависимости от различных вариантов использования
- Пользователи могут отключать предупреждения на очень мелком уровне
Например: мотивацией для jOOQ будет устранение неоднозначности метода DSL equal()
из неудачного Object.equals()
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public interface Field<T> { /** * <code>this = value</code>. */ Condition equal(T value); /** * <strong>Watch out! This is * {@link Object#equals(Object)}, * not a jOOQ DSL feature!</strong> */ @Override @Warning ( name = "ACCIDENTAL_EQUALS" , description = "Did you mean Field.equal?" ) boolean equals(Object other); } |
- Предпосылки этого варианта использования описаны здесь: https://github.com/jOOQ/jOOQ/issues/4763
Вывод
JEP 277 полезен, без сомнения. Но он также очень ограничен по объему (вероятно, чтобы не задерживать Jigsaw?). Тем не менее, я бы хотел, чтобы эта тема генерации таких предупреждений компилятора была более подробно рассмотрена сопровождающими JDK. Это прекрасная возможность для DoTheRightThing ™
Я не думаю, что вышеупомянутая «спецификация» завершена. Это просто грубая идея. Но я много раз мечтал о таком механизме как дизайнер API. Чтобы иметь возможность дать пользователям подсказку о возможном злоупотреблении API, которое они могут отключить с помощью:
-
@SuppressWarnings
, прямо в коде. - Легко реализовать настройки IDE. Для Eclipse, NetBeans и IntelliJ было бы очень просто реализовать настраиваемую обработку предупреждений для этих вещей.
Как только у нас @Warning
аннотация @Warning
, мы можем, наконец, отказаться от не очень полезного @Deprecated
…
1
2
3
|
@Warning (name = "OBSOLETE" ) public @interface Deprecated { } |
Ссылка: | JEP 277 «Усиленный износ» — это Ницца. Но вот гораздо лучшая альтернатива от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |