递归静态枚举和单例定义

问题描述 投票:0回答:2

TL;DR:不要做我所做的事情。运行并阅读下面最小重现器中的代码注释。

我有一个

enum
,它采用
class
的实例,它本身是一个从
enum
获取值的单例。仅仅阅读该内容就表明这是一个糟糕的设计。

下面我们可以看到一些不一致的结果;例如,在调用

MyEnum.VALUE.myClassInstance
之前打印
MyClass.getInstance()
会导致
MyEnum.VALUE.myClassInstance
为非
null
,但在打印
MyClass.getInstance()
之前调用
MyEnum.VALUE.myClassInstance
会导致
MyEnum.VALUE.myClassInstance
为空。

但是为什么??

public class StrangeEnumFailure {
    
    public static void main(final String[] args) {
        // Enabling any of the following three lines will cause the prints to print the expected values
        
        // MyEnum.VALUE.getMyClassInstance();
        // MyEnum.VALUE.getClass();
        // System.out.println(MyEnum.VALUE.myClassInstance); // Not null!
        
        System.out.println(MyClass.getInstance());             // Not null
        System.out.println(MyEnum.VALUE.myClassInstance);      // null... but only if we do it after MyClass.getInstance()!
        System.out.println(MyClass.INSTANCE);                  // Not null
        System.out.println(MyEnum.VALUE.getMyClassInstance()); // null
        System.out.println(MyEnum.VALUE.myClassInstance);      // null
    }
    
    public static class MyClass {
        
        static final MyClass INSTANCE = new MyClass(MyEnum.VALUE);
        
        final MyEnum enumValue;
        
        private MyClass(final MyEnum enumValue) {
            this.enumValue = enumValue;
        }
        
        static MyClass getInstance() {
            return INSTANCE;
        }
    }
    
    public enum MyEnum {
        VALUE(MyClass.INSTANCE);
        
        final MyClass myClassInstance;
        
        MyEnum(final MyClass myClassInstance) {
            this.myClassInstance = myClassInstance;
        }
        
        MyClass getMyClassInstance() {
            return this.myClassInstance;
        }
    }
}

(不要评判代码,非私有字段和缺少 JavaDoc 只是为了方便演示)

Java版本信息:

openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment Temurin-17.0.3+7 (build 17.0.3+7)
OpenJDK 64-Bit Server VM Temurin-17.0.3+7 (build 17.0.3+7, mixed mode, sharing)
java enums static singleton classloader
2个回答
0
投票

该构造导致类加载依赖于顺序(总体)。

如果你要求 Java 首先使用

MyClass

  • 加载
    MyClass
    • 设置
      INSTANCE
      • 加载
        MyEnum
        • 得到
          MyClass.INSTANCE
        • MyClass.INSTANCE == null
          ,因为它还没有设置,因为我们实际上正在设置它,所以
        • 设置
          this.myClassInstance = null
      • 设置
        this.enumValue = MyEnum.VALUE

因此,在此加载序列结束时,

INSTANCE
不是
null
,但当我们在实例化null值时抓取它时,
enum

另一方面,如果你要求 Java 首先使用

MyEnum

  • 加载
    MyEnum
    • 得到
      MyClass.INSTANCE
      • 加载
        MyClass
        • 设置
          INSTANCE
          • 获取
            MyEnum.VALUE
            ,我们当前正在实例化
          • 设置
            this.myEnumValue = MyEnum.VALUE
        • INSTANCE
          现在是非
          null
    • set
      this.myClassInstance = MyClass.INSTANCE
      where
      MyClass.INSTANCE != null
      因为我们只是在加载时构造了它
      MyClass

因此,一切都按预期设置。

我无意中这样做了,并且将恢复它,因为这显然是一个糟糕的设计选择。

别这样做。


0
投票

通常,在 Java 中,不需要 单例模式

尽管经过一些研究,似乎应该在类上使用 enum
考虑到它本质上是单个实例。

enum MyClass {
    instance;

    String s;

    static MyClass getInstance() {
        return instance;
    }

    String getS() {
        return s;
    }

    void setS(String s) {
        this.s = s;
    }

    @Override
    public String toString() {
        return Objects.toIdentityString(this);
    }
}

enum MyEnum {
    VALUE(MyClass.instance);

    final MyClass myClassInstance;

    MyEnum(final MyClass myClassInstance) {
        this.myClassInstance = myClassInstance;
    }

    MyClass getMyClassInstance() {
        return this.myClassInstance;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.