samedi 22 juillet 2017

Instantiating an unknown Object whose constructor takes a primitive array as an argument (reflection)

This program randomly instantiates any Object by recursively iterating through the parameters of a constructor until it finds a primitive or wrapper parameter that it can randomize. However, when a constructor takes a primitive array as a parameter, I get a

java.lang.IllegalArgumentException: argument type mismatch.

I have poured through Stack Overflow and the internet, but can't seem to find out how to feed a wrapper array into a constructor or turn a wrapper array into a primitive array.

I have considered the following methods of fixing this, but I am not sure of how to implement them:

  1. Create a custom subclass of java.lang.reflect.Constructor and change the getParameterTypes() method so that if a given parameter is a primitive array (such as int[]), change it to Integer[]. I think this may have some adverse effects however.
    1. Figure out how I can "add" items to varargs/ the Object[] parameterValues iteratively so that I can inspect each parameterValues[i] as I come across it, see if it is a wrapper array, then change it to a primitive array. This brings up the problem that a primitive array, such as a byte[], cannot go into the parameterValues that is fed into constructor.newInstance(parameterValues).

You may read all of the code if you'd like, however the two enums are not particularly relevant. The bulk of the action and my problem occurs in nextRandom(Class<?> cls) and some of it in randomBase(BASE_TYPE baseType).

Code:

import java.lang.reflect.*;
import java.util.Random;

/**
 * Sources used:
 * http://ift.tt/2eEQN16
   object-is-of-primitive-type
 * http://ift.tt/2gTGqr9
   contains-a-given-string
 * http://ift.tt/2eEOA5C
   random-number-in-java
 *
 * Created by Ward Bradt/ BlueOxile on July 22, 2017
 * http://ift.tt/2gTeh3c
 */ 
public class RandomGenerator {

    public enum BASE_TYPE {
        INTEGER (int.class, Integer.class),
        DOUBLE (double.class, Double.class),
        FLOAT (float.class, Float.class),
        LONG (long.class, Long.class),
        SHORT (short.class, Short.class),
        BYTE (byte.class, Byte.class),
        BOOLEAN (boolean.class, Boolean.class),
        CHAR (char.class, Character.class);

        private final Class<?> primitiveClass;
        private final Class<?> wrapperClass;

        BASE_TYPE(Class<?> primitive, Class<?> wrapper) {
            this.primitiveClass = primitive;
            this.wrapperClass = wrapper;
        }

        public Class<?> getPrimitiveClass() {
            return primitiveClass;
        }

        public Class<?> getWrapperClass() {
            return wrapperClass;
        }
    }

    public enum BASE_TYPE_ARRAY {
        INTEGER (int[].class, Integer[].class, BASE_TYPE.INTEGER),
        DOUBLE (double[].class, Double[].class, BASE_TYPE.DOUBLE),
        FLOAT (float[].class, Float[].class, BASE_TYPE.FLOAT),
        LONG (long[].class, Long[].class, BASE_TYPE.LONG),
        SHORT (short[].class, Short[].class, BASE_TYPE.SHORT),
        BYTE (byte[].class, Byte[].class, BASE_TYPE.BYTE),
        BOOLEAN (boolean[].class, Boolean[].class, BASE_TYPE.BOOLEAN),
        CHAR (char[].class, Character[].class, BASE_TYPE.CHAR);

        private final Class<?> primitiveArrayClass;
        private final Class<?> wrapperArrayClass;
        private final BASE_TYPE baseType;

        BASE_TYPE_ARRAY(Class<?> primitiveArray, Class<?> wrapperArray, BASE_TYPE b) {
            this.primitiveArrayClass = primitiveArray;
            this.wrapperArrayClass = wrapperArray;
            this.baseType = b;
        }

        public Class<?> getPrimitiveClass() {
            return baseType.getPrimitiveClass();
        }

        public Class<?> getPrimitiveArrayClass() {
            return primitiveArrayClass;
        }

        public Class<?> getWrapperClass() {
            return baseType.getWrapperClass();
        }

        public Class<?> getWrapperArrayClass() {
            return wrapperArrayClass;
        }

        public BASE_TYPE getBaseType() {
            return baseType;
        }
    }

    public static Object nextRandom(Class<?> cls) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        // base case: primitive array or wrapper array
        if (cls.isArray()) {
            for (BASE_TYPE_ARRAY i : BASE_TYPE_ARRAY.values()) {
                if (cls.equals(i.getPrimitiveArrayClass()) || cls.equals(i.getWrapperArrayClass())) {
                    // later: random wrapper of cls if primitive
                    // later: is slightly inefficient because we iterate over BTA.values() than iterate in
                    // randomBase using a switch statement.
                    return randomBaseArray(i);
                }
            }
        }
        // base case: if primitive or wrapper
        else {
            for (BASE_TYPE i : BASE_TYPE.values()) {
                if (cls.equals(i.getPrimitiveClass()) || cls.equals(i.getWrapperClass())) {
                    // later: random wrapper array of cls
                    return randomBase(i);
                }
            }
        }

        Constructor<?> constructor = cls.getConstructors()[0];
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        Object[] parameterValues = new Object[parameterTypes.length];

        for (int i = 0; i < parameterValues.length; i++) {
            // Recursively creates objects/ parameters for constructor
            parameterValues[i] = nextRandom(parameterTypes[i]);
        }

        // ----------------------
        // EXCEPTION THROWN HERE
        // ----------------------
        return constructor.newInstance(parameterValues);
    }

    public static Object randomBase(BASE_TYPE baseType) {
        Random rand = new Random();
        switch (baseType) {
            case BYTE:
                return randomByte();
            case CHAR:
                return randomChar();
            case LONG:
                return rand.nextLong();
            case FLOAT:
                return rand.nextFloat();
            case DOUBLE:
                return rand.nextDouble();
            case SHORT:
                return randomShort();
            case BOOLEAN:
                return rand.nextBoolean();
            case INTEGER:
                return rand.nextInt();
        }
        throw new IllegalArgumentException();
    }

    public static Object[] randomBaseArray(BASE_TYPE_ARRAY baseTypeArray) {
        short srt = (short) (1 + new Random().nextInt(Short.MAX_VALUE));
        Object arr = Array.newInstance(baseTypeArray.getWrapperClass(), srt);

        for (int i = 0; i < Array.getLength(arr); i++) {
            Array.set(arr, i, randomBase(baseTypeArray.getBaseType()));
        }
        return (Object[])arr;
    }

    public static Byte randomByte() {
        byte[] b = new byte[1];
        new Random().nextBytes(b);
        return b[0];
    }

    public static Character randomChar() {
        return (char)(new Random().nextInt(85) + 32);
    }

    public static Short randomShort() {
        return (short) (Short.MIN_VALUE + new Random().nextInt(Short.MAX_VALUE * 2 + 2));
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        // I don't fully know how to use @SuppressWarnings yet, so most of my methods throw these exceptions^
        String str = (String)nextRandom(String.class);
    }
}





Aucun commentaire:

Enregistrer un commentaire