Search code examples
javagenericstype-erasure

Generic Type From Enum & The Builder Pattern


I'm trying to create a builder pattern that uses generics to provide type checking on some of the methods. Currently I have the following working:

ParameterBuilder.start(String.class).setName("foo").setDefaultValue("Hello").build();
ParameterBuilder.start(Integer.class).setName(bar).setDefaultValue(42).build();
ParameterBuilder.start(Boolean.class).setName(bar).setDefaultValue(false).build();

Using the code:

public class ParameterBuilder<T> {
  private String name;
  private T defaultValue;

  public static <T2> ParameterBuilder<T2> start(Class<T2> type) {
    return new ParameterBuilder<T2>();
  }
  // Other methods excluded for example
}

So the type of the input for the setDefaultValue method is defined by what's passed into the start method, just as I want.

But now I want to extend what's being passed into start() to contain a little more information. Essentially I want to pass in a "type" for the parameters I creating. Sometimes these parameters will be things like "email", "url" etc. The default value will still be of a known type (String in those cases), so I'd like to have something like:

ParameterBuilder.start(EMAIL).setName("email").setDefaultValue("[email protected]").build();
ParameterBuilder.start(URL).setName("website").setDefaultValue("http://www.somewhere.com").build();

Where at the moment EMAIL & URL are enums, containing amongst other things - the class of the default value. But if I go down this route, how would I instantiate the parameter builder?

public static <T2> ParameterBuilder<T2> start(ParameterType paramType) {
  Class<T2> type = paramType.getTypeClass();
  // How do I instantiate my ParameterBuilder with the right type?
}

If it can't be done using enums (which I can see being the case), does anyone have a suggestion for a different solution?


Solution

  • I think you need one enum per class type (I don't see how you could have one enum cover several types and keep the thing working). In that case, a common generic interface could do what you want. You can then create some sort of factory to provide the enum constants if that helps.

    This compiles:

    static interface ParameterType<T> {}
    
    static enum ParameterTypeEnum implements ParameterType<String> { EMAIL; }
    
    public static void main(String[] args) {
        ParameterBuilder
               .start(ParameterTypeEnum.EMAIL)
               .setName("email")
               .setDefaultValue("[email protected]")
               .build();
    }
    
    public static class ParameterBuilder<T> {
    
        private String name;
        private T defaultValue;
    
        public static <T2> ParameterBuilder<T2> start(ParameterType<T2> paramType) {
            return new ParameterBuilder<T2>();
        }
    
        ParameterBuilder<T> setName(String name) {
            this.name = name;
            return this;
        }
    
        ParameterBuilder<T> setDefaultValue(T defaultValue) {
            this.defaultValue = defaultValue;
            return this;
        }
    
        void build() {}
    }