Статьи

JEP 277 «Усиленный износ» — это Ницца.

Поддерживать API сложно.

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

Когда вы читаете комментарии Брайана Гетца и других о том, как поддерживать обратную совместимость в JDK, я могу только выразить большое уважение к их работе. Очевидно, что мы все хотим, чтобы такие вещи, как Vector , Stack , Hashtable были окончательно удалены, но есть некоторые крайние случаи, связанные с обратной совместимостью, вокруг API коллекций, о которых обычные смертные никогда не подумают. Например: почему Java Collections не удаляют общие методы?

Лучше амортизация

Стюарт Маркс aka Dr Deprecator

Стюарт Маркс aka Dr Deprecator

С 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);
}

Вывод

JEP 277 полезен, без сомнения. Но он также очень ограничен по объему (вероятно, чтобы не задерживать Jigsaw?). Тем не менее, я бы хотел, чтобы эта тема генерации таких предупреждений компилятора была более подробно рассмотрена сопровождающими JDK. Это прекрасная возможность для DoTheRightThing ™

Я не думаю, что вышеупомянутая «спецификация» завершена. Это просто грубая идея. Но я много раз мечтал о таком механизме как дизайнер API. Чтобы иметь возможность дать пользователям подсказку о возможном злоупотреблении API, которое они могут отключить с помощью:

  • @SuppressWarnings , прямо в коде.
  • Легко реализовать настройки IDE. Для Eclipse, NetBeans и IntelliJ было бы очень просто реализовать настраиваемую обработку предупреждений для этих вещей.

Как только у нас @Warning аннотация @Warning , мы можем, наконец, отказаться от не очень полезного @Deprecated

1
2
3
@Warning(name = "OBSOLETE")
public @interface Deprecated {
}