j-easy/easy-random

Use discovered non default constructor by generating random paramters for it

astubbs opened this issue · 2 comments

I've been looking but can't see it - what's the reason for not reflecting and using a discovered constructor that is not the default? Is there some design reason why this hasn't been done? If not - I'd be happy to submit a PR.

Something like:

    class ReflectionConstructor implements ObjectFactory {
        @Override
        public <T> T createInstance(Class<T> type, RandomizerContext context) {
                return (T) constructReflection(type, context);
        }


    @SneakyThrows
    private <T> Object constructReflection(Class<T> type, RandomizerContext context) {
        Constructor<?> complexConstructor = findComplexConstructor(type);
        Object[] params = createParams(complexConstructor);
        Object instance = complexConstructor.newInstance(params);
        return instance;
    }

    private Object[] createParams(Constructor<?> constructor) {
        Object[] randomParams = Arrays.stream(constructor.getParameters()).map(parameter ->
                easyRandom.nextObject(parameter.getType())
        ).toArray();
        return randomParams;
    }

    /**
     * @eturn the constructor with the most number of arguments
     */
    private <T> Constructor<?> findComplexConstructor(Class<T> type) {
        Constructor<?>[] constructors = type.getConstructors();
        int numParams = 0;
        Constructor<?> complex = null;
        for (Constructor<?> constructor : constructors) {
            int parameterCount = constructor.getParameterCount();
            if (parameterCount > numParams) {
                numParams = parameterCount;
                complex = constructor;
            }
        }
        return complex;
    }

    }

what's the reason for not reflecting and using a discovered constructor that is not the default? Is there some design reason why this hasn't been done?

The reason is that the library was initially designed to randomize Java Beans, which should provide a default constructor. But even if the target type does not provide a default constructor, ER will still try to randomize it by falling back to objenesis. The issue is that objenesis bypasses any constructor..

Therefore, if you want to take control over the initialization process, you should use a custom object factory as you did and showed in your last comment. This is mentioned in the docs here: Types with no default constructor.