维护API很难。
我们正在维护非常复杂的jOOQ API。 但是就语义版本而言,我们遵循相对宽松的规则 。
当您阅读Brian Goetz和其他人关于在JDK中保持向后兼容性的评论时,我只能对他们的工作表示敬意。 显然,我们都希望最终移除Vector
, Stack
, Hashtable
类的东西,但是在collection API周围存在与向后兼容相关的边缘情况,普通凡人不会想到。 例如: Java集合为什么不删除通用方法?
更好的弃用
使用Java 9,Jigsaw和模块化,这些新功能的主要驱动目标之一是能够“切断” JDK的各个部分,并在以后的发行版中轻轻地弃用并删除它们。 作为此改进的一部分, Stuart Marks AKA Deprecator博士建议了JEP 277:“增强的弃用” 。
这样做的目的是通过一些附加信息来增强@Deprecated
批注,例如:
- 不确定的 。 该API已被弃用,没有给出任何理由。 这是默认值; 今天隐含弃用的所有内容都有一个不赞成使用的弃用原因。
- 谴责 。 该API已指定在将来的JDK版本中删除。 请注意,此处使用的“已谴责”一词在意欲拆除的结构的意义上使用。 该术语并不意味着暗示任何道德谴责。
- 危险的 。 使用此API可能导致数据丢失,死锁,安全漏洞,错误结果或JVM完整性丢失。
- 过时的 。 不再需要此API,应删除用法。 不存在替代API。 请注意,OBSOLETE API可能会或可能不会标记为CONDEMNED。
- 放弃 。 该API已被较新的API取代,用法应从该API迁移到较新的API。 请注意,SUPERSEDED API可能会或可能不会被标记为CONDEMNED。
- 已取消 。 调用无效或将无条件引发异常。
- 实验 。 该API并不是规范的稳定部分,它可能会不兼容地更改或随时消失。
在弃用东西时,能够传达弃用的意图很重要。 也可以通过@deprecated
Javadoc标记来实现,该标记可以生成任何类型的文本。
另一种更好的解决方案
上述主张存在以下问题:
- 它是不可扩展的 。 对于JDK库设计人员来说,以上内容可能就足够了,但是作为第三方API提供程序的我们将希望枚举中包含更多元素,而不是CONDEMNED,DANGEROUS等。
- 仍然没有纯文本信息 。 由于我们仍无法正式向注释提供任何可澄清的文本,例如为什么某事物“危险”的动机,因此该注释与Javadoc标记之间仍然存在冗余。
- “不推荐使用”是错误的 。 将UNIMPLEMENTED或EXPERIMENTAL标记为“已弃用”的想法表明了该JEP的变通方法性质,它试图在现有名称中增加一些新功能。
我感到JEP太害怕触摸太多部分了。 但是,将有一个非常简单的替代方案,它对每个人都好得多:
public @interface Warning {
String name() default "warning";
String description() default "";
}
无需将可能的警告类型的数量限制为有限的常量列表。 相反,我们可以使用@Warning
注释,该注释可以包含任何字符串!
当然,JDK可以具有一组众所周知的字符串值,例如:
public interface ResultSet {
@Deprecated
@Warning(name="OBSOLETE")
InputStream getUnicodeStream(int columnIndex);
}
要么…
public interface Collection<E> {
@Warning(name="OPTIONAL")
boolean remove(Object o);
}
注意,虽然实际上不赞成使用JDBC的ResultSet.getUnicodeStream()
,但我们也可以向Collection.remove()
方法添加提示,该方法仅适用于Collection
类型,而不适用于其许多子类型。
现在,使用这种方法的有趣之处在于,我们还可以增强有用的@SuppressWarnings
批注,因为有时,我们只是知道KnowWhatWeAreDoing™,例如在编写类似以下内容时:
Collection<Integer> collection = new ArrayList<>();
// Compiler!! Stop bitching
@SuppressWarnings("OPTIONAL")
boolean ok = collection.remove(1);
这种方法可以一次性解决许多问题:
- JDK维护人员拥有他们想要的。 轻度弃用JDK的好工具
- 关于
@SuppressWarnings
可能发生的事情的记录不完整的混乱最终将变得更加干净和正式 - 我们可以根据各种用例向用户发出大量自定义警告
- 用户可以在非常细微的级别上使警告静音
例如: jOOQ的动机是消除DSL equal()
方法与不幸的Object.equals()
方法之间的歧义:
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
批注,我们也许可以最后淘汰不那么有用的@Deprecated
…
@Warning(name = "OBSOLETE")
public @interface Deprecated {
}