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

标签 java design-patterns singleton

<分区>

著名的 BalusC 答案 (https://stackoverflow.com/a/2707195) 指向的核心 Java 类:

java.lang.Runtime#getRuntime()
java.awt.Desktop#getDesktop()
java.lang.System#getSecurityManager()

以上所有似乎都是具有私有(private)构造函数的类,而不是枚举。如果枚举是实现单例模式的最佳实践 (Why is Enum best implementation for Singleton),为什么它没有在核心 Java 源代码中使用?

最佳答案

枚举背后的语义

我认为无论 Java 版本如何,我们都不应该将 Java 枚举视为创建类的一个且唯一一个实例的独特方法。
枚举传达一个含义:为特定实体定义的一组枚举值。
我不鼓励将其用作在其中执行大量逻辑(远程调用、持久性等......)的单例。
从语义上讲,当在某些代码中使用枚举时,代码的读者不会期望其中包含这样的逻辑。

这里使用的 Bill Pugg 习惯用法和急切的初始化是枚举的一个很好的替代方法,例如在 Runtime 类中:

private static Runtime currentRuntime = new Runtime();
private Runtime() {}

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

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

一些没有使用枚举的单例类的例子:

桌面(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);
    }
  //...
}

不要仅从这种情况下得出结论,因为这是具有惰性初始化的特例。

集合(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(); }
}

IsoChronology(JDK 8):

public final class IsoChronology extends AbstractChronology implements Serializable {

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

OptionalIntDeserializer(JDK 8):

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

    static final OptionalIntDeserializer INSTANCE = new OptionalIntDeserializer();

}

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

比较器(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();
    }
}

我让您从这些示例中得出结论。

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

更进一步,单例是通过显式硬编码实现的反模式:您可以通过引入一个接口(interface)来避免它,就像使用老式方法(静态急切或惰性单例)的枚举一样实现一个工厂方法来检索它。

但最终,所有这些都需要始终如一的严谨性(因此容易出错)并且倾向于使用样板代码(因此会使您的代码变得不那么有意义)。
依赖注入(inject)解决了这个问题。所以我认为创建单例的最好方法是最终不显式创建单例。

关于java - 为什么核心 Java 库不使用枚举来实现单例模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56714806/

相关文章:

java - 计算 java 中字符串的行数 - BufferedReader 行为

java - switch 中的新实例

c# - 在 C# 中使用静态构造函数创建单例

java - 编写 Util 类的最佳方式

java - 为什么在使用 Guice 时,Java 中的 @Singleton 的 @Inject 总是导致 null?

java - 如何在java中获取参数的注释?

java - 尝试使用正确的行标题创建 JTable

design-patterns - 如何设计游戏服务器的通信 channel

java - (Android) 如何使用模式设置操作栏样式

java - BigDecimal.ZERO 与新的 BigDecimal(0)。使用哪个,为什么?