ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-11-11 18:14:11  阅读:249  来源: 互联网

标签:multithreading lazy-initialization java


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

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有