简体   繁体   中英

Java reflection invoke method with varargs

I am having an issue with reflection to calculate how many arguments the user passed and compare the length of arguments to the expected count. If this requirement is met then proceed to execute the method with an Object[] { incommingArguments } (varargs) argument to the method invoker.

I have a method called compute() in my LogicGates class. The method takes 2 to 3 arguments. The first argument is the gate and the next 1 or 2 are the inputs depending on the inputs that the gate takes.

Currently, the method with the signature:

compute(gate:GATE,a:boolean,b:boolean):boolean

executes correctly, but if I use the

compute(gate:GATE,<VARARGS>:boolean):boolean

signature then my code breaks. I am using the logic for varargs that I found here: StackOverflow: Invoke method with an array parameter using reflection .

Both methods can be found at the bottom of LogicGates.java at the end of my question.

Output

 A | B | AND     A | B | OR     A | B | NAND     A | B | NOR     A | B | XOR     A | B | XNOR     A | B | XNOR_NAND     A | B | XNOR_NOR     A | B | IF_THEN     A | B | THEN_IF    
---+---+-----   ---+---+----   ---+---+------   ---+---+-----   ---+---+-----   ---+---+------   ---+---+-----------   ---+---+----------   ---+---+---------   ---+---+---------   
 T | T | T       T | T | T      T | T | F        T | T | F       T | T | F       T | T | T        T | T | T             T | T | T            T | T | T           T | T | T          
 T | F | F       T | F | T      T | F | T        T | F | F       T | F | T       T | F | F        T | F | F             T | F | F            T | F | F           T | F | T          
 F | T | F       F | T | T      F | T | T        F | T | F       F | T | T       F | T | F        F | T | F             F | T | F            F | T | T           F | T | F          
 F | F | F       F | F | F      F | F | T        F | F | T       F | F | F       F | F | T        F | F | T             F | F | T            F | F | T           F | F | T          

GateTest.java

public class GateTest {
    public static void main(String[] args) {
        StringBuffer[] rows = newStringBufferArray(6, "");

        for (LogicGates.GATE g : LogicGates.GATE.values()) {
            if (g.operands == 2)
                rows = fillTable(g, rows, repeat(' ', 3));
        }

        for (StringBuffer row : rows) {
            System.out.println(row);
        }
    }

    private static final StringBuffer[] fillTable(final LogicGates.GATE gate,
            StringBuffer[] buffer, String delimiter) {
        int l = buffer.length - 1;
        String name = gate.name();
        String pad = repeat('-', name.length() + 1);

        buffer[0].append(String.format(" A | B | %s %s", name, delimiter));
        buffer[1].append(String.format("---+---+-%s%s", pad, delimiter));

        for (byte b = 3; b >= 0; --b)
            buffer[l - b].append(fillRow(gate, intToBool(b >> 1),
                    intToBool(b & 1), delimiter));

        return buffer;
    }

    private static final String fillRow(final LogicGates.GATE gate, boolean a,
            boolean b, String delimiter) {
        return String.format(" %c | %c | %c%s %s", boolToChar(a),
                boolToChar(b),
                boolToChar(LogicGates.compute(gate, a, b)),
                repeat(' ', gate.name().length() - 1), delimiter);
    }

    public static final StringBuffer[] newStringBufferArray(int size,
            String initialValue) {
        if (initialValue == null)
            initialValue = "";

        StringBuffer[] bufferArr = new StringBuffer[size];

        for (int i = 0; i < size; i++)
            bufferArr[i] = new StringBuffer(initialValue);

        return bufferArr;
    }

    private static final String repeat(char ch, int count) {
        StringBuilder sb = new StringBuilder();

        while (sb.length() < count)
            sb.append(ch);

        return sb.toString();
    }

    private static final char boolToChar(final boolean bool) {
        return bool ? 'T' : 'F';
    }

    private static final boolean intToBool(final int input) {
        return intToBool((byte) input);
    }

    private static final boolean intToBool(final byte input) {
        if (input < 0 || input > 1)
            throw new IllegalArgumentException("Input must be 0 or 1");

        return input == 1;
    }
}

LogicGates.java

import java.lang.reflect.Method;
import java.util.Arrays;

@SuppressWarnings("unused")
public final class LogicGates {
    public static enum GATE {
        NOT("not", "Negation", 1), // .
        AND("and", "Logical conjunction", 2), // .
        OR("or", "Logical disjunction", 2), // .
        NAND("nand", "Logical NAND", 2), // .
        NOR("nor", "Logical NOR", 2), // .
        XOR("xor", "Exclusive disjunction", 2), // .
        XNOR("xnor", "Logical biconditional", 2), // .
        XNOR_NAND("xnorNand", "XNOR using only NAND gates.", 2), // .
        XNOR_NOR("xnorNor", "XNOR using only NOR gates.", 2), // .
        IF_THEN("ifThen", "Material implication.", 2), // .
        THEN_IF("thenIf", "Converse implication.", 2); // .

        private String methodName, description;
        int operands;

        private GATE(String methodName, String description, int operands) {
            this.methodName = methodName;
            this.description = description;
            this.operands = operands;
        }

        protected String methodName() {
            return methodName;
        }

        protected int operands() {
            return operands;
        }
    }

    private LogicGates() {
        throw new AssertionError();
    }

    private static final boolean not(boolean a) {
        return !a;
    }

    private static final boolean and(final boolean a, final boolean b) {
        return a && b;
    }

    private static final boolean or(final boolean a, final boolean b) {
        return a || b;
    }

    private static final boolean nand(final boolean a, final boolean b) {
        return not(and(a, b));
    }

    private static final boolean nor(final boolean a, final boolean b) {
        return not(or(a, b));
    }

    private static final boolean xor(final boolean a, final boolean b) {
        return or(and(a, not(b)), and(not(a), b));
    }

    private static final boolean xnor(final boolean a, final boolean b) {
        return or(and(a, b), nor(a, b));
    }

    private static final boolean xnorNand(final boolean a, final boolean b) {
        return nand(nand(nand(a, nand(a, b)), nand(b, nand(a, b))),
                nand(nand(a, nand(a, b)), nand(b, nand(a, b))));
    }

    private static final boolean xnorNor(final boolean a, final boolean b) {
        return nor(nor(a, nor(a, b)), nor(b, nor(a, b)));
    }

    private static final boolean ifThen(final boolean a, final boolean b) {
        return or(and(a, b), not(a));
    }

    private static final boolean thenIf(final boolean a, final boolean b) {
        return or(a, nor(a, b));
    }

    public static final boolean compute(GATE gate, boolean... values) {
        boolean result = false;

        if (values.length != gate.operands())
            throw new IllegalArgumentException(String.format(
                    "%s gate requires %d inputs.", gate.name(), gate.operands));

        try {
            Class<?> c = Class.forName(LogicGates.class.getName());
            Method method = null;

            if (gate.operands() == 2)
                method = c.getDeclaredMethod(gate.methodName(), boolean.class,
                        boolean.class);
            else if (gate.operands() == 1) {
                method = c.getDeclaredMethod(gate.methodName(), boolean.class);
            } else {
                method = null;
            }

            boolean[] args = Arrays.copyOfRange(values, 0, values.length);
            result = (boolean) method.invoke(c, new Object[] { args });
        } catch (Exception e) {
            System.out.println(e.getMessage() + " " + e.getCause());
        }

        return result;
    }

    public static final boolean compute(GATE gate, boolean a, boolean b) {
        boolean result = false;

        try {
            Class<?> c = Class.forName(LogicGates.class.getName());
            Method method = null;

            if (gate.operands() == 2)
                method = c.getDeclaredMethod(gate.methodName(), boolean.class,
                        boolean.class);
            else if (gate.operands() == 1) {
                method = c.getDeclaredMethod(gate.methodName(), boolean.class);
            } else {
                method = null;
            }

            result = (boolean) method.invoke(c, a, b);
        } catch (Exception e) {
            System.out.println(e.getMessage() + " " + e.getCause());
        }

        return result;
    }
}

In Java 7, use the invokeWithArguments method.

Edit: of course, this implies using the Java 7 MethodHandle class in place of the old Java 1.1 Method class.

Well, after I sat down for a bit and thought this through, I came up with a solution:

Output

>> Invoking SimpleGate.not(false)
>> Invoking SimpleGate.and(true,true)
>> Invoking SimpleGate.or(true,false)
>> Invoking SimpleGate$AdvancedGate.xor(true,false)
PASS

ReflectTest.java

public class ReflectTest {
    private enum Gate {
        NOT(SimpleGate.class),
        AND(SimpleGate.class),
        OR(SimpleGate.class),
        XOR(SimpleGate.AdvancedGate.class);

        private Class<?> clazz;

        private Gate(Class<?> clazz) { this.clazz = clazz; }

        public Class<?> clazz() { return clazz; }
    }

    public static boolean compute(Gate gate, Boolean... input) {
        return (Boolean) Reflect.call(gate.clazz(),
                gate.name().toLowerCase(), (Object[]) input);
    }

    public static void main(String[] args) {
        Boolean not = compute(Gate.NOT, false);
        Boolean and = compute(Gate.AND, true, true);
        Boolean or = compute(Gate.OR, true, false);
        Boolean xor = compute(Gate.XOR, true, false);

        if (not && and && or && xor)
            System.out.println("PASS");
        else
            System.out.println("FAIL");
    }
}

Reflect.java

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public abstract class Reflect {
    public static final Object call(Class<?> clazz, String methodName, Object... varArgs) {
        try {
            Object invokee = null;
            String className = clazz.getName();
            int argc = varArgs.length;
            Class<?>[] classList = new Class[argc];
            StringBuffer varArgsStr = new StringBuffer();

            for (int i = 0; i < argc; i++) {
                Object argv = varArgs[i];
                classList[i] = argv.getClass();
                varArgsStr.append(argv.toString());

                if (i < argc - 1) varArgsStr.append(',');
            }
            System.out.printf(">> Invoking %s.%s(%s)\n", className,methodName, varArgsStr);

            Class<?> target = Class.forName(className);
            Method method = target.getDeclaredMethod(methodName, classList);
            int childIndex = className.indexOf('$');

            if (childIndex > -1) {
                String wrappingClassName = className.substring(0, childIndex);
                Class<?> wrapper = Class.forName(wrappingClassName);
                Object wrapperInstance = wrapper.newInstance();
                Constructor<?> con = target.getDeclaredConstructor(wrapper);

                invokee = con.newInstance(wrapperInstance);
            } else {
                boolean isStatic = Modifier.isStatic(method.getModifiers());

                invokee = isStatic ? target : target.newInstance();
            }

            return method.invoke(invokee, varArgs);

        } catch(Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

SimpleGate.java & SimpleGate$AdvancedGate.java

public class SimpleGate {
    public Boolean not(Boolean a) { return !a; }

    public static Boolean and(Boolean a, Boolean b) { return a && b; }

    public static Boolean or(Boolean a, Boolean b) { return a || b; }

    public class AdvancedGate {
        public Boolean xor(Boolean a, Boolean b) {
            return or(and(not(a),b),and(a,not(b)));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM