I have a method which accepts a class parameter. I need to create a list of this class objects for any kind of a class. Each class may have a constructor with various number of parameters.
This is what I managed to get so far. It works, but obviously it is not generic enough. Is there a more generic code for this code?
public List<Object> convertSeparatedListToJsonList(String pipedStr, String separatorRegex, Class c){
try {
Class<?> cCtor = Class.forName(c.getTypeName());
List<String> valuesList = Stream.of(pipedStr.split(separatorRegex)).map(String::trim).collect(Collectors.toList());
int valuesNumber = valuesList.size();
switch (valuesNumber) {
case 1 -> {
Constructor<?> ctor = cCtor.getConstructor(String.class);
return List.of(ctor.newInstance(valuesList.get(0)));
}
case 2 -> {
Constructor<?> ctor = cCtor.getConstructor(String.class, String.class);
return List.of(ctor.newInstance(valuesList.get(0), valuesList.get(1)));
}
case 4 -> {
Constructor<?> ctor = cCtor.getConstructor(String.class, String.class, String.class, String.class);
return List.of(ctor.newInstance(valuesList.get(0), valuesList.get(1), valuesList.get(2), valuesList.get(3)));
}
case 5 -> {
Constructor<?> ctor = cCtor.getConstructor(String.class, String.class, String.class, String.class, String.class);
return List.of(ctor.newInstance(valuesList.get(0), valuesList.get(1), valuesList.get(2), valuesList.get(3), valuesList.get(4)));
}
default -> {
return new ArrayList<>();
}
}
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage());
return new ArrayList<>();
}
}
My goal: to generalize the switch part for all list sizes.
CodePudding user response:
That getConstructor
method call is already taking an array of classes. Same with newInstance
so all you need to do is initialize an array of size valuesNumber
that is filled with String.class and then convert your valuesList to an array. Here are some useful links:
Fastest way to set all values of an array?
CodePudding user response:
If I understand correctly, this should work:
public List<Object> convertSeparatedListToJsonList(String pipedStr, String separatorRegex, Class<?> cCtor){
try {
List<String> valuesList = Stream.of(pipedStr.split(separatorRegex)).map(String::trim).collect(Collectors.toList());
int valuesNumber = valuesList.size();
Class<?>[] constructorParameters = new Class<?>[valuesNumber];
Arrays.fill(constructorParameters, String.class);
Constructor<?> ctor = cCtor.getConstructor(constructorParameters);
return List.of(ctor.newInstance(valuesList.toArray()));
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
return List.of();
}
}
I've changed the last parameter to not use a raw type, and I've removed the use of Class.forName
, which seems redundant.
To call getConstructor
, you can create an array of size valuesNumber
and fill
it with String.class
, then pass that array.
Similarly, to call newInstance
, you can just convert valuesList
to an array.
This works because variable arity parameters (e.g. the parameter of getConstructor
) have array types as their formal parameter types, and Java prefers a non-variable arity invocation over a variable arity invocation. This means that ctor.newInstance(valuesList.toArray())
will actually be understood as "call the constructor with the parameters in this array", rather than "call the constructor with the array as the single parameter".