Allow instancing of classes with non-empty constructor
See original GitHub issueIs your feature request related to a problem? Please describe. Kotlin data classes (even of embedded objects) must have initatizers for their values:
This works fine
data class My class(val value : Int = 0)
However, this does not
data class My class(val value : Int)
The problem lies in the instance creator which always tries to find a parameterless constructor to instanciate the classes with. If this is missing morphia throws an error.
Mappers like Gson and Jackson have work arounds for this. Morphia should be able to do the same.
Consider adding this great class to the instance creator of no suitable constructor is found:
Another solution could be to fetch the constructor with the least number or parameters and try to insert their default values if primitives or use it recursively if not. I have a PoC for this which works great. Here it is
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toMap;
public class ConstructorlessInstance {
private static final Map<Class<?>, Object> DEFAULT_VALUES = Stream
.of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class)
.collect(toMap(clazz -> (Class<?>) clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0)));
public static <T> T create(final Class<? super T> rawType) throws IllegalAccessException, InvocationTargetException, InstantiationException {
var constructor = Arrays.stream(rawType.getDeclaredConstructors()).min(Comparator.comparingInt(Constructor::getParameterCount)).orElseGet(() -> {
throw new IllegalStateException("Found no constructors");
});
var arguments = new Object[constructor.getParameterCount()];
createDefaultArguments(arguments, constructor.getParameters());
constructor.setAccessible(true);
//noinspection unchecked
return (T) constructor.newInstance(arguments);
}
private static void createDefaultArguments(Object[] arguments, Parameter[] parameters) throws IllegalAccessException, InvocationTargetException, InstantiationException {
for (int i = 0; i < parameters.length; i++) {
arguments[i] = createDefaultArgument(parameters[i]);
}
}
private static Object createDefaultArgument(Parameter parameter) throws IllegalAccessException, InstantiationException, InvocationTargetException {
var type = parameter.getType();
if (type.isPrimitive()) {
return DEFAULT_VALUES.get(type);
}
if (type.isAssignableFrom(String.class)) {
return "";
}
return create(type);
}
}
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:7 (7 by maintainers)
I thought i had an issue open for this already. It’s often on my mind anyway. This is a priority issue for me, personally.
@Constructor
is no longer required (or even used) and@Name
annotations are optional now and only needed when the constructor argument name does not match the field name. Of course, for data classes those are one and the same. Details will be up on the morphia.dev website once the docs sync completes in about 20 minutes.