假设您有一个静态的无参数方法,该方法是幂等的,并且始终返回相同的值,并且可能会抛出一个已检查的异常,如下所示:

class Foo {
 public static Pi bar() throws Baz { getPi(); } // gets Pi, may throw
}


现在,如果构造返回的Object的东西很昂贵且永不改变,则这是懒惰单身人士的理想人选。一种选择是Holder模式:

class Foo {
  static class PiHolder {
   static final Pi PI_SINGLETON = getPi();
  }
  public static Pi bar() { return PiHolder.PI_SINGLETON; }
}


不幸的是,这是行不通的,因为我们无法从(隐式)静态初始值设定项块中抛出检查后的异常,因此我们可以尝试这样的操作(假设我们希望保留调用者在调用时获得检查后的异常的行为)致电bar()):

class Foo {
  static class PiHolder {
   static final Pi PI_SINGLETON;
   static {
    try {
     PI_SINGLETON =  = getPi(); }
    } catch (Baz b) {
     throw new ExceptionInInitializerError(b);
    }
  }

  public static Pi bar() throws Bar {
   try {
    return PiHolder.PI_SINGLETON;
   } catch (ExceptionInInitializerError e) {
    if (e.getCause() instanceof Bar)
     throw (Bar)e.getCause();
    throw e;
   }
}


在这一点上,也许双重检查锁定更干净?

class Foo {
 static volatile Pi PI_INSTANCE;
 public static Pi bar() throws Bar {
  Pi p = PI_INSTANCE;
  if (p == null) {
   synchronized (this) {
    if ((p = PI_INSTANCE) == null)
     return PI_INSTANCE = getPi();
   }
  }
  return p;
 }
}


DCL仍然是反模式吗?我在这里还缺少其他解决方案吗(也可以使用诸如单次检查的次要变体,但是从根本上不改变解决方案)吗?是否有充分的理由选择一个?

我没有尝试上面的示例,因此很有可能它们没有被编译。

编辑:我没有重新实现或重新构造此单例的使用者(即Foo.bar()的调用者)的奢侈,也没有机会引入DI框架来解决此问题。我最感兴趣的是在给定的约束范围内解决问题的解决方案(提供一个已检查异常的单例,并传播到调用方)。

更新:我决定最终选择DCL,因为它提供了保存现有合同的最干净的方法,而且没有人提供避免避免DCL正确执行的具体原因。我没有在公认的答案中使用该方法,因为这似乎是实现同一件事的一种过于复杂的方法。

最佳答案

本质上,“保持”技巧是由JVM执行的双重检查锁定。根据规范,类初始化处于(两次检查)锁定之下。不幸的是,JVM可以安全(快速)地进行DCL,Java程序员无法使用此功能。我们能做的最接近的是通过中介最终参考。请参阅DCL上的维基百科。

您保留异常的要求并不难:

class Foo {
  static class PiHolder {
    static final Pi PI_SINGLETON;
    static Bar exception;
    static {
      try {
        PI_SINGLETON =  = getPi(); }
      } catch (Bar b) {
        exception = b;
      }
    }
  }
public Pi bar() throws Bar {
  if(PiHolder.exception!=null)
    throw PiHolder.exception;
  else
    return PiHolder.PI_SINGLETON;
}

10-06 07:24