I have two similar classes, Foo and Bar
public class Foo{ private String name; public Foo(String name){ this.name = name; } } public class Bar{ private String name; public Bar(String name){ this.name = name; } }
And I’ve got two methods in another class that creates a Set of Foo (1st method) and Bar (2nd method) and are pretty much the same. The first one:
private Set<Foo> buildFoos(final List<String> names) { final Set<Foo> set = new HashSet<>(); for (int i = 0; i < names.size(); i++) { set.add(new Foo(names.get(i))); } return set; }
And the second one:
private Set<Bar> buildBars(final List<String> names) { final Set<Bar> set = new HashSet<>(); for (int i = 0; i < names.size(); i++) { set.add(new Bar(names.get(i))); } return set; }
As you can see, both methods are pretty much the same so I thought I could use generics to use only one method. My approach would be something like this:
private <T> Set<T> build(final Class<T> clazz, final List<String> names) { final Set<T> set = new HashSet<>(); for (int i = 0; i < names.size(); i++) { set.add(clazz.getConstructor(String.class).newInstance(names.get(i))); } return set; }
I’ve tested this and it’s supposed to be working but my question is, what would happen if Foo or Bar would have a different constructor (so for instance, Bar would have another String as the 2nd parameter). All I can think about is to check the instance of the class and pick one of the two constructors (something like this)
private <T> Set<T> build(final Class<T> clazz, final List<String> names) { final Set<T> set = new HashSet<>(); for (int i = 0; i < names.size(); i++) { if(clazz.isInstance(Foo.class){ set.add(clazz.getConstructor(String.class).newInstance(names.get(i))); } else if(clazz.isInstance(Bar.class){ set.add(clazz.getConstructor(String.class, String.class).newInstance(names.get(i), "example")); } } return set; }
Is there a better way of achieving this? Is this even a good practice? Any tip is always appreciated. Thanks in advance!
Advertisement
Answer
It seems to me that it would be better to take a Function<String, T>
instead of a Class<T>
. That way you don’t need reflection, and callers can pass in constructors, factory methods, lambdas that use multi-parameter constructors, whatever they want. So your method would be:
private <T> Set<T> build(final Function<String, T> factory, final List<String> names) { final Set<T> set = new HashSet<>(); for (String name : names) { set.add(factory.apply(name)); } return set; }
I suspect this could be written more simply using streaming, but that’s a separate issue. (I did take the opportunity to use an enhanced-for loop for simplicity though.)