编程语言
首页 > 编程语言> > Java 7中的获取或创建模式?

Java 7中的获取或创建模式?

作者:互联网

我正在尝试编写一种通用功能,该功能可按需执行线程安全的可选惰性初始化.我不能使用标准模式,因为该值不是最终值,并且可能已经通过设置器设置了.

Java 8中,我通过与供应商一起编写了一个通用的LazyInitializer解决了这一问题:

public class LazyInitializer<T> {

  protected final Supplier<T> initializer;
  protected AtomicReference<T> value = new AtomicReference<>();

  public LazyInitializer(final Supplier<T> initializer) {
    this.initializer = initializer;
  }

  public T get() {
    T result = this.value.get();
    if (result == null) {
      this.value.compareAndSet(null, this.initializer.get());
      result = this.value.get();
    }
    return result;
  }

  public void setValue(final T value) {
    this.value.set(value);
  }

}

然后,您将使用此类:

final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList<String>::new);

这是线程安全的,可以处理setter,开销非常低,尤其是:需要很少的样板代码.

但是,现在我不得不使用Java 7,而且似乎找不到一个同样优雅的解决方案,因为Java 7无法使用Suppliers,因此需要编写许多难看的代码.同样,除非您提供确切的类,否则泛型不允许实例化它们,如果您使用泛型类值(例如ArrayList< String>),这是一个问题.

据我所知,我要么被迫编写丑陋的代码,要么被我的能力之外做反射魔术,或者有什么方法可以用Java 7中的优雅方式编写LazyInitializer类,而我却不见了?

编辑:使用来自Jorn Vernee的答案,我将类修改为与Java 7兼容,如下所示:

public class LazyInitializer<T> {

  protected final Class<?> clazz;
  protected AtomicReference<T> value = new AtomicReference<>();

  public LazyInitializer(final Class<?> clazz) {
    this.clazz = clazz;
  }

  public T get() {
    T result = this.value.get();
    if (result == null) {
      this.value.compareAndSet(null, constructNew());
      result = this.value.get();
    }
    return result;
  }

  public void setValue(final T value) {
    this.value.set(value);
  }

  protected T constructNew() {
    try {
      return (T) clazz.newInstance();
    } catch (InstantiationException | IllegalAccessException ex) {
      throw new IllegalStateException(ex);
    }
  }
}

然后可以(再次)优雅地调用它,如下所示:

final LazyInitializer<List<String>> value = new LazyInitializer<>(ArrayList.class);

但是,此类不再能够验证所提供的类是否真正匹配(因为泛型),并且仅适用于默认构造函数.但这至少解决了我的情况.

解决方法:

关于lambdas / method refs的一件好事是,它减少了X的代码量.因此,如果您回头,当然,它将再次使X的代码量增加.如果您需要将任何代码包装在函子中,则匿名类是实现它的最佳方法.

还有另一种使用反射的效率较低,更骇人的方法.只要您按预期使用它,它就不会抛出异常.

您可以进行动态构造函数查找,您仍然需要Supplier类型:

interface Supplier<T> {
    T get();
}

然后,您有一个工厂方法在运行时进行查找:

public static <T> Supplier<T> constructorLookup(Class<?> rawtype) {
    try {
        Constructor<?> cons = rawtype.getConstructor();

        return new Supplier<T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T get() {
                try {
                    return (T) cons.newInstance();
                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                        | InvocationTargetException e) {
                    throw new IllegalStateException(e);
                }
            }               
        };

    } catch (NoSuchMethodException | SecurityException e) {
        throw new IllegalArgumentException(e);
    }       
}

结果代码如下所示:

LazyInitializer<List<String>> value 
    = new LazyInitializer<>(constructorLookup(ArrayList.class));

当前,这仅适用于默认构造函数,但也可以扩展为与参数一起使用.

标签:multithreading,lazy-initialization,java
来源: https://codeday.me/bug/20191111/2021598.html