Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/400.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中的可配置枚举_Java_Enums_Lazy Initialization - Fatal编程技术网

Java中的可配置枚举

Java中的可配置枚举,java,enums,lazy-initialization,Java,Enums,Lazy Initialization,我正在寻找一种更好的模式来实现如下内容: public static enum Foo { VAL1( new Bar() ), VAL2( new FooBar() ); private final bar; private Foo( IBar bar ) { this.bar = bar; } public IBar getBar() { return bar; } } public interface Factory

我正在寻找一种更好的模式来实现如下内容:

public static enum Foo {
    VAL1( new Bar() ),
    VAL2( new FooBar() );

    private final bar;

    private Foo( IBar bar ) {
        this.bar = bar;
    }

    public IBar getBar() { return bar; }
}
public interface Factory {
   IBar getBar();
}

public class BarFactory implements Factory {

   private IBar barInstance;   

   public synchronized IBar getBar() {
       if (barInstance == null) {
          barInstance = new Bar();
       }
       return barInstance;
   }
}

public class FooBarFactory implements Factory {

   private IBar barInstance;   

   public synchronized IBar getBar() {
       if (barInstance == null) {
          barInstance = new FooBar();
       }
       return barInstance;
   }
}
public static enum Foo {
  VAL1(new BarHolder() {
    IBar createBar() { return new Bar(); }
  )},
  VAL2(new BarHolder() {
    IBar createBar() { return new FooBar(); }
  )};

  private final BarHolder barHolder;

  private Foo(BarHolder barHolder) {
    this.barHolder = barHolder;
  }

  public IBar getBar() { return barHolder.getBar(); }
}
问题是访问
enum
会产生副作用。说
Bar
打开一个数据库连接等。因此,即使我只需要
VAL2
,我也必须为设置
VAL1
付出代价

另外,
bar
的值与
enum
紧密耦合。它类似于一个静态属性,但是
enum
没有延迟初始化。我可以将
Foo.getBar()
抽象化并使用匿名类,但是每次我都必须支付安装费用

有没有一种便宜的方法可以为
enum
s的属性添加lazy init

[编辑]要明确这一点:

  • getBar()
    被调用了数百万次。它一定很快就失明了

  • 我们这里讲的是单例(就像
    enum
    本身一样)。只能创建一个实例

    对于其他点,单元测试应该能够覆盖此行为

  • 实例必须惰性地创建

  • 我们尝试的一个解决方案是在Spring中将值注册为bean:

    <bean id="VAL1.bar" class="...." />
    
    
    
    这允许我们在运行时指定值,并在测试中重写它们。不幸的是,这意味着我们必须以某种方式将
    ApplicationContext
    注入
    enum
    。所以我们需要一个全局变量。畏缩

    更糟糕的是:在
    getBar()
    中查找值太慢了。如果(bar!=null)bar=context.get(name()+“.bar”),我们可以
    同步
    并使用
    来解决这个问题


    但是,有没有一种方法没有这个功能,可以像使用
    enum
    值本身一样安全和快速?

    尝试在枚举中存储一个类而不是一个对象,并且在需要时,只需通过class.newInstance()实例化它

    公共静态枚举Foo{
    VAL1(巴级),
    VAL2(FooBar.class);
    私人最后一班;
    
    privatefoo(Class只需用抽象工厂模式替换enum

    UPD:您可以尝试以下方式:

    public static enum Foo {
        VAL1( new Bar() ),
        VAL2( new FooBar() );
    
        private final bar;
    
        private Foo( IBar bar ) {
            this.bar = bar;
        }
    
        public IBar getBar() { return bar; }
    }
    
    public interface Factory {
       IBar getBar();
    }
    
    public class BarFactory implements Factory {
    
       private IBar barInstance;   
    
       public synchronized IBar getBar() {
           if (barInstance == null) {
              barInstance = new Bar();
           }
           return barInstance;
       }
    }
    
    public class FooBarFactory implements Factory {
    
       private IBar barInstance;   
    
       public synchronized IBar getBar() {
           if (barInstance == null) {
              barInstance = new FooBar();
           }
           return barInstance;
       }
    }
    
    public static enum Foo {
      VAL1(new BarHolder() {
        IBar createBar() { return new Bar(); }
      )},
      VAL2(new BarHolder() {
        IBar createBar() { return new FooBar(); }
      )};
    
      private final BarHolder barHolder;
    
      private Foo(BarHolder barHolder) {
        this.barHolder = barHolder;
      }
    
      public IBar getBar() { return barHolder.getBar(); }
    }
    

    您可以尝试以某种方式优化同步部分,但这种方式可能会根据具体的用例而有所不同。

    您可以使用“值持有者”添加一级间接寻址,该值持有者执行延迟初始化:

    abstract class BarHolder {
      IBar bar;
    
      abstract IBar createBar();
    
      IBar getBar() {
        if (bar == null) {
          bar = createBar();
        }
        return bar;
      }
    }
    
    现在,按如下方式调整枚举:

    public static enum Foo {
        VAL1( new Bar() ),
        VAL2( new FooBar() );
    
        private final bar;
    
        private Foo( IBar bar ) {
            this.bar = bar;
        }
    
        public IBar getBar() { return bar; }
    }
    
    public interface Factory {
       IBar getBar();
    }
    
    public class BarFactory implements Factory {
    
       private IBar barInstance;   
    
       public synchronized IBar getBar() {
           if (barInstance == null) {
              barInstance = new Bar();
           }
           return barInstance;
       }
    }
    
    public class FooBarFactory implements Factory {
    
       private IBar barInstance;   
    
       public synchronized IBar getBar() {
           if (barInstance == null) {
              barInstance = new FooBar();
           }
           return barInstance;
       }
    }
    
    public static enum Foo {
      VAL1(new BarHolder() {
        IBar createBar() { return new Bar(); }
      )},
      VAL2(new BarHolder() {
        IBar createBar() { return new FooBar(); }
      )};
    
      private final BarHolder barHolder;
    
      private Foo(BarHolder barHolder) {
        this.barHolder = barHolder;
      }
    
      public IBar getBar() { return barHolder.getBar(); }
    }
    

    警告:由于这是非线程安全的,因此可以创建任意多个
    IBar
    实例。因此,此解决方案不满足单例要求(#OP中的2)。更糟糕的是,
    getBar()
    可以根据Vladimir的回答轻松返回对尚未初始化的IBar实例的引用-这可以做到。它只会创建类对象,实例应根据需要延迟创建:

    public static enum Foo {
        VAL1(Bar.class),
        VAL2(FooBar.class);
    
        private Class<? extends IBar> clazz;
        private IBar bar = null;
    
        private Foo( Class<? extends IBar> clazz) {
            this.clazz = clazz;
        }
    
        public IBar getBar() {
          if (bar == null)
             bar = clazz.newInstance();
          }
          return bar;
        }
    }
    
    公共静态枚举Foo{
    VAL1(巴级),
    VAL2(FooBar.class);
    
    私有类以下是线程安全的惰性初始化的,结合了两种模式:和。这是唯一的惰性的方法,以避免不必要的同步,在模式发生的情况下,每个
    getBar()
    调用,而在这种情况下,它只在初始化时发生一次,而初始化是通过以下方式进行的:


    我认为这是最好的解决方案。你有代码吗?如何确保每个值只创建一次(像单例一样)?这并不能解决我的问题,因为每次调用
    getBar()
    ,它都会创建一个新实例。它只创建一次实例(请参见BarHolder.getBar())。不使用
    synchronize
    ,您能使此工作多线程吗?这是一种类似于原始值的访问(在循环中和代码中的任何地方都有数百万次)而且我担心成本。客户端如何确保每个值只创建一个实例?它们是否都必须在同一个枚举中?如果使用不同的枚举,每个枚举只能加载一个(或一个组)它们都属于同一个枚举。从逻辑/语义的角度来看,将它们分散在多个枚举上是没有意义的。