抛出AbstractMethodError的经典方法VS抽象方法



我刚刚看了一下TimeUnit枚举源代码(在下面简化):

public enum TimeUnit {
    SECONDS {
        public long toMillis(long d) { return d * 1000L; }
    },
    MINUTES {
        public long toMillis(long d) { return d * 60000L; }
    };
    public long toMillis(long duration) {
        throw new AbstractMethodError();
    }
}

他们也可以使用一种抽象的方法来实现它:

public enum TimeUnit {
    SECONDS {...}, MINUTES {...};
    public abstract long toMillis(long duration);
}

既然他们选择了第一个实现,我想一定有原因。因此,我的问题是:为什么?AbstractMethodError可以抛出吗?如果是,在哪种情况下?

标准doclet(即Sun/Oracle变体)在Javadoc输出中不提供abstract枚举方法。如果这些方法是抽象的,Javadoc将不会显示它们的踪迹。这个错误仍然存在,最初报告于2005年(9年多前):

JDK-6287639:枚举类中的抽象方法不应列为抽象方法http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6287639

如果要实现正确的行为,这些方法将出现在Javadoc输出中,去掉抽象关键字。枚举值本质上是枚举类的匿名子类,因此javac确保枚举值实现每个抽象方法。因此,从外部观察者的角度来看,抽象枚举方法实际上是非抽象的。

以下在TimeUnit实现中编写的注释提供了更多的见解:

// To maintain full signature compatibility with 1.5, and to improve the
// clarity of the generated javadoc (see 6287639: Abstract methods in
// enum classes should not be listed as abstract), method convert
// etc. are not declared abstract but otherwise act as abstract methods.

如果Javadoc对您的项目很重要,您可能需要考虑追随TimeUnit的脚步。

只能在其中一个枚举值未能覆盖该方法或调用超类方法时抛出异常。我不推荐那种特殊的方法;让编译器捕获这些错误要好得多,就像您建议的替代方案一样。

顺便说一句,MINUTEStoMillis实现在我看来肯定是错误的。这不是我在Java 7源代码中的实现。你在哪里找到的?

附言:我刚刚检查了一下:TimeUnit的Android实现没有使用AbstractMethodError,但也没有使用抽象方法。它使用了各种表,并在enum级别实现了所有的转换方法。

最新更新