javafunctiongenericsconstructor

Is there a better way of building this <T> object?


For the method below, is there a better way of building the returned object? It's of type <T>.

The first input parameter has the same type as the returned object.

The last builder parameter is what makes me anxious. It currently points to a static factory method in each T class.

If you wanted to avoid passing the builder param, how would you handle that?

(I would prefer not to resort to a cast, and to use a technique that is friendly towards immutable objects.)

public <T extends FourVector> T transformVector(T input, TransformInto direction, Function<Map<Axis, Double>, T> builder) {
  //the core calculation uses matrices, so we convert back and forth like so
  Matrix input_matrix = Matrix.asMatrix(input);
  Matrix result = physicsMatrix(direction.sign()).times(input_matrix);
  //I need to build a new T object here; hence the builder param
  return builder.apply(asComponents(result));
}

Solution

  • I experimented with about 6 different variations.

    The best I could come up with involves creating an explicit Builder interface for building the desired object.

    The basic idea is that the incoming input object is of the correct type T already, so it makes sense to ask that object to build a new object of the same type T.

    private <T extends FourVector & Builder<T>> T transformVector(T input, TransformInto direction) {
        //the core calculation uses matrices, so we convert back and forth like so
        Matrix input_matrix = Matrix.asMatrix(input);
        Matrix output_matrix = lambdaMatrix(direction.sign()).times(input_matrix);
        return input.build(fromComponents(output_matrix));
      }
    

    With the Builder interface as the translator from the lower-level data structure to the desired higher-level data structure:

    public interface Builder<T> {
      
      /** Build a new T object out of the given components. */
      public T build(Map<Axis, Double> components);
    
    }
    

    The unusual thing about this is that object creation is not through a constructor, but through a regular object method.