When writing reusable code, we often want it to be as general and flexible as possible. So layers of abstractions are added; generic types; abstract class hierarchies; and let’s not forget the Factory pattern. Joel Spolsky had a famous rant about the factory factory factory pattern, and it can get ugly in the real world as well.

One of the reasons for the often clunky factory class is that it has not been possible to pass methods and constructors. Method references in Java 8 changes that, even for constructors. They can be passed for any class or array, e.g. String::new, String[]::new. Combined with generics, the type of the newly created object can also be specified.

In the example class below, the constructor happen to take two arguments, first a String and then an int. Therefore, the BiFunction function method fits, however, it would probably be more appropriate to define a more specific functional interface, which would also make the code more readable. The return value is of the type T, which should then be the same type as the generated object. The use is demonstrated in the test method below.

The restriction with this setup is of course that the number of arguments to the constructor is fixed. We could write fixes around that as well, but that would require the general class to know something about the classes it is instantiating, which defetes the purpose. There’s always the old Factory class, though.

  class Generator<T> {

    private final BiFunction<String, Integer, T> constructor;

    Generator(BiFunction<String, Integer, T> constructor) {
      this.constructor = constructor;
    }

    T generate() {
      return constructor.apply(TEST, _123);
    }
  }

  public void testGenerateA() {
    Generator<A> genA = new Generator<>(A::new);
    A a = genA.generate();
    assertEquals(TEST + _123, a.content);
  }

Now, it can be argued that this still constitutes a factory pattern, even if an external factory class is not used. The methods of Collectors highlights this, e.g.:

    public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
        return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                                   (r1, r2) -> { r1.addAll(r2); return r1; },
                                   CH_ID);
    }

The full code listing of the example:

GenericConstructor.java
GitHub Raw
/* Copyright rememberjava.com. Licensed under GPL 3. See http://rememberjava.com/license */
package com.rememberjava.lambda;

import static org.junit.Assert.assertEquals;

import java.util.function.BiFunction;

import org.junit.Test;

public class GenericConstructor {

  private static final String TEST = "test";
  private static final int _123 = 123;

  class Generator<T> {

    private final BiFunction<String, Integer, T> constructor;

    Generator(BiFunction<String, Integer, T> constructor) {
      this.constructor = constructor;
    }

    T generate() {
      return constructor.apply(TEST, _123);
    }
  }

  class A {
    final String content;

    A(String str, int i) {
      content = str + i;
    }
  }

  class B {
    final String str;
    final int i;

    B(String str, int i) {
      this.str = str;
      this.i = i;
    }
  }

  @Test
  public void testGenerateA() {
    Generator<A> genA = new Generator<>(A::new);
    A a = genA.generate();
    assertEquals(TEST + _123, a.content);
  }

  @Test
  public void testGenerateB() {
    Generator<B> genB = new Generator<>(B::new);
    B b = genB.generate();
    assertEquals(TEST, b.str);
    assertEquals(_123, b.i);
  }
}