Search code examples
javamultithreadinglazy-initialization

Get-or-create pattern in Java 7?


I am trying to write a generic functionality that does thread-safe optional lazy initialization on demand. I cannot use the standard pattern, as the value is not final and could have been set via a setter already.

In Java 8 I solved that by writing a generic LazyInitializer with a supplier:

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);
  }

}

You would then use this class like this:

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

This is thread-safe, can handle setters, has a very low overhead and especially: requires little boilerplate code.

Now however I am forced to use Java 7 and I cannot seem to find an equally elegant solution, as Java 7 cannot use Suppliers and therefore requires to write a lot of ugly code. Also generics do not allow to instanciate them unless you supply the exact class, which is a problem if you use generic class values (like ArrayList<String>).

As far as I can tell I am either forced to write ugly code or do reflection magic beyond my capabilites, or is there any way to write the LazyInitializer class in an elegant way in Java 7 that I am missing?

Edit: Using the Answer from Jorn Vernee I modified the class to work with Java 7 as follows:

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);
    }
  }
}

Which can then be (once again) elegantly called like this:

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

However this class can no longer verify if the supplied class actually matches (because Generics) and it only works with default constructors. But at least it solves my case.


Solution

  • One of the nice things about lambdas/method refs, is that it reduced the amount of code by X. So if you go back, of course, it's going to increase the amount of code by X again. If you need to wrap any code in a functor, an anonymous class is the best way to do it imho.

    There is another less efficient, hackier way to do this that uses reflection. And as long as you use it as intended, it will not throw exceptions.

    You can do a dynamic constructor lookup, you'd still need the Supplier type:

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

    Then you have a factory method that does the lookup at runtime:

    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);
        }       
    }
    

    The resulting code would look like:

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

    Currently this only works for default constructors, but could be expanded to work with arguments too.