为什么核心Java库不使用枚举来实现单例模式?

tod*_*not 5 java singleton design-patterns

著名的BalusC答案(/sf/answers/189503681/)指向的核心Java类:

java.lang.Runtime#getRuntime()
java.awt.Desktop#getDesktop()
java.lang.System#getSecurityManager()
Run Code Online (Sandbox Code Playgroud)

以上所有内容似乎都是带有私有构造函数的类,而不是枚举。如果enum是实现单例模式的最佳实践(为什么Enum是Singleton的最佳实现),为什么在核心Java源代码中未使用它?

dav*_*xxx 6

枚举后面的语义

我认为我们不应该将Java枚举看作创建类的一个实例和一个实例的唯一方法,而不管Java版本如何。
枚举传达了一种含义:为特定实体定义的一组枚举值。
我不鼓励将它用作在内部执行很多逻辑(远程调用,持久性等等)的单例。
从语义上来说,当在某些代码中使用枚举时,代码的读者不会期望其中包含这种逻辑。

此处使用的Bill Pugg惯用语进行了热切的初始化,它是枚举的一种合理替代方法,例如在Runtimeclass中:

private static Runtime currentRuntime = new Runtime();
private Runtime() {}
Run Code Online (Sandbox Code Playgroud)

JDK 5(出现枚举功能的版本)或更高版本的源代码中的Singleton实现

我发现没有枚举模式的单例实现比使用它更频繁。

一些没有枚举用法的单例类示例:

Desktop(JDK 6)。

public static synchronized Desktop getDesktop(){
   //...
   Desktop desktop = (Desktop)context.get(Desktop.class);
    if (desktop == null) {
        desktop = new Desktop();
        context.put(Desktop.class, desktop);
    }
  //...
}
Run Code Online (Sandbox Code Playgroud)

不要仅仅从这种情况下得出结论,因为这是一个带有延迟初始化的特例。

Collections (JDK 1.7):

/**
 * Returns an enumeration that has no elements.  More precisely,
 *
  ...
 * @since 1.7
 */
@SuppressWarnings("unchecked")
public static <T> Enumeration<T> emptyEnumeration() {
    return (Enumeration<T>) EmptyEnumeration.EMPTY_ENUMERATION;
}

private static class EmptyEnumeration<E> implements Enumeration<E> {
    static final EmptyEnumeration<Object> EMPTY_ENUMERATION
        = new EmptyEnumeration<>();

    public boolean hasMoreElements() { return false; }
    public E nextElement() { throw new NoSuchElementException(); }
    public Iterator<E> asIterator() { return emptyIterator(); }
}
Run Code Online (Sandbox Code Playgroud)

IsoChronology(JDK 8):

public final class IsoChronology extends AbstractChronology implements Serializable {

    /**
     * Singleton instance of the ISO chronology.
     */
    public static final IsoChronology INSTANCE = new IsoChronology();
}
Run Code Online (Sandbox Code Playgroud)

OptionalIntDeserializer(JDK 8):

public class OptionalIntDeserializer extends BaseScalarOptionalDeserializer<OptionalInt>
{
    private static final long serialVersionUID = 1L;

    static final OptionalIntDeserializer INSTANCE = new OptionalIntDeserializer();

}
Run Code Online (Sandbox Code Playgroud)

而且,您很少会发现使用枚举模式实现的单例。

Comparators (JDK 1.8):

enum NaturalOrderComparator implements Comparator<Comparable<Object>> {
    INSTANCE;

    @Override
    public int compare(Comparable<Object> c1, Comparable<Object> c2) {
        return c1.compareTo(c2);
    }

    @Override
    public Comparator<Comparable<Object>> reversed() {
        return Comparator.reverseOrder();
    }
}
Run Code Online (Sandbox Code Playgroud)

我让你从这些例子中得出结论。

编写单例代码:通常是反模式

百尺竿头更进一步,单身是当通过硬编码它明确地实现反模式:你可以通过引入接口枚举避免像用老式的方法(静态渴望或懒惰单),它实现 一个工厂方法来检索它。

但是最后,所有这些都需要持续的严格性(容易出错),并且倾向于样板代码(因此使您的代码不那么有意义)。
依赖注入解决了这个问题。因此,我认为创建单例的最佳方法是最终不要显式创建单例。

  • 期待意外。 (2认同)

Jör*_*tag 5

因为他们没有时间机器。

enums在2004 java.lang.runtime.Runtime#getRuntime()年以Java 5“ Tiger” 引入。在1996 java.lang.runtime.System#getSecurityManager()年以Java 1.0引入。在1996 年以Java 1.0引入。

java.awt.Desktop#getDesktop()于2006年在Java 6“ Mustang”中引入,因此从理论上讲它可以使用enums。您必须问设计师为什么选择了这种特殊设计。

  • 我的猜测是,即使Java团队也似乎避免避免“滥用”“枚举”类型,是因为“枚举”在设计时考虑了不同的意图:枚举(因此命名为_enum_)。Oracle:“枚举类型是一种特殊的数据类型,它使变量成为一组预定义的常量。” 这是每个人看到“枚举”(枚举变量)时所期望的。没有人会期望背后有一个复杂的类。我认为只是不要将“ enum”用于其他用途。对具有大量“枚举”类型的库进行映像,您不知道它们是类还是普通枚举。 (3认同)