java.reflection.Method.invoke(...)不重新生成args

时间:2014-02-08 06:24:00

标签: java swing reflection

我正在尝试使用Java Reflection API。我只是将任何给定类的Method对象提取到JComboBox中,并在它的itemSelected上,为参数创建一个接口(当然还有一个调用对象。)

这很好,没有发布。

但是在invokeButton的动作中,我试图用给定的参数调用所选方法。

最初它说参数数量不同。我被我的一个朋友指导说paramVals数组引用了实际值,这可能导致问题,可能是由于范围。然后我开始创建Object类的新对象,然后为它们分配值。这适用于param计数。但现在的问题是参数不是正确的类型转换。即使是对象的字符串类型转换(因为它必须是一个对象数组)也不会被强制转换回String。

doc说,invoke方法会将它们强制转换为自己的,如果转换失败,将抛出IllegalArgumentException。

我没有得到导致调用方法调用失败的原因......

以下是框架的代码:

package nttraining.abhay.reflectiondemo;
//imports go here

public class ReflectionFrame 
    extends JFrame
    implements ActionListener, ItemListener{

    JComboBox methods;
    JButton invokeButton;

    public ReflectionFrame(String title) throws HeadlessException {
        super(title);
        //Layout components

        //adding methods of class String to a combo
        Class<String> c = String.class;
        Method ml[] = c.getMethods();
        for(Method m : ml){
            methods.addItem(m);
        }

        invokeButton.addActionListener(this);
        methods.addItemListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource().equals(invokeButton)){
            Method selected = (Method) methods.getSelectedItem();
            Class paramtypes[] = selected.getParameterTypes();
            Object paramVals[] = new Object[paramtypes.length];
            System.out.println("Method : " + selected.toString());
            for(int i=0; i<paramtypes.length; i++){
                Object obj = new Object();
                obj = paramtypes[i].cast(params[i].getText());
                paramVals[i] = obj;
                System.out.println("Added " + paramtypes[i].cast(params[i].getText()).toString() + " to params");
            }
            try {
                result.setText(selected.invoke(object.getText(), params).toString());

            } catch (Exception ex) {
                System.out.println(ex.getClass().getName() + ": " + ex.getMessage());
            }
        }
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        Method selected = (Method) methods.getSelectedItem();
        if(selected==null)
            return;
        Class paramtypes[] = selected.getParameterTypes();
        int paramCount = paramtypes.length;
        object = new JTextField();
        paramNames = new JLabel[paramCount];
        params = new JTextField[paramCount];
        panel.removeAll();
        panel.setLayout(new GridLayout(paramCount+1, 2));
        panel.add(new JLabel("Calling object"));
        panel.add(object);
        for(int i=0; i<paramCount; i++){
            paramNames[i] = (JLabel) panel.add(new JLabel(paramtypes[i].getName()));
            params[i] = (JTextField) panel.add(new JTextField());
        }
        invalidate();
        validate();
    }

}

2 个答案:

答案 0 :(得分:0)

我发现的问题在于这一行:
obj = paramtypes[i].cast(params[i].getText());
cast不转换对象,它只验证给定对象是否属于某个类。由于您始终提供String.class作为参数(通过.getText()),因此除了String类型参数之外的任何其他内容都将失败。即使Integer.class到原始int也会失败。

下面是一段演示cast问题的代码。

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class Q21642768 {

public static void main(String[] args) {

    try {
        // Calls String.indexOf(str, fromIndex) via reflection.
        callStringMethod("Hello reflection world", "reflection", 1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void callStringMethod(String s, String subString, int startIndex) throws Exception {

    Class<String> c = String.class;
    Method ma[] = c.getMethods();
    Method indexOfSub = null;
    Class<?>[] indexOfSubPTypes = null;
    List<Method> stringMethods = new ArrayList<Method>();
    List<Type[]> stringMethodsPTypes = new ArrayList<Type[]>();
    for (Method m: ma) {
        stringMethods.add(m);
        System.out.print(m.getName() + ": ");
        Class<?>[] mptypes = m.getParameterTypes();
        stringMethodsPTypes.add(mptypes);
        boolean first = true;
        for (Type t : mptypes) {
            if (first) {
                first = false;
            } else {
                System.out.print(", ");
            }
            System.out.print(t.toString());
        }
        if ("indexOf".equals(m.getName()) 
                && mptypes.length == 2 
                && mptypes[0].equals(String.class)
                && mptypes[1].equals(int.class)) {
            indexOfSub = m;
            indexOfSubPTypes = mptypes;
            System.out.println(" <-- ");
        } else {
            System.out.println();
        }
    }
    if (indexOfSub == null) {
        System.out.println("target method not found");
        return;
    }
    Object[] pValues = new Object[2];
    pValues[0] = indexOfSubPTypes[0].cast(subString);
    // Fails:
    // pValues[1] = indexOfSubPTypes[1].cast(startIndex);
    // pValues[1] = indexOfSubPTypes[1].cast(startIndex + "");
    pValues[1] = startIndex;

    Object result = indexOfSub.invoke(s, pValues);
    System.out.println("Result: " + result);
}

}

答案 1 :(得分:0)

问题是所有参数值都是String对象,因为您使用JTextField.getText()获取它们。 String是这些值的运行时类型,而方法参数的类型通常是不同的,这才是最重要的。

要成功调用该方法,首先需要将每个值转换为paramTypes数组中指定的正确类型。无论是cast()还是invoke()都不会为你做到这一点。这意味着你必须找到一种方法来从String进行转换,基本上是从String值反序列化为正确类的对象,并且这可能并不总是可行或太复杂。在这一点上,我认为你可以开始想象你想要做的事情的复杂性。这远非微不足道。请记住,每个参数值通常不是一个简单的值,而是一个完整的对象图,这就是复杂性所在。

例如,如果方法参数的类型是接口,您将如何知道实例化哪个具体实现?如果您确实找到了实现它的具体类 - 并且可能并不总是可行 - 您将如何创建该类的实例?在这里,您将进入Java中的序列化框架所涵盖的域。这些框架中有很多是开源的,你可能想看看它们中的一些。你会找到一份包含这些脆弱文件的综合清单here

几年前,我参与了一个相关项目,在那里我必须提供一个Swing GUI,使最终用户能够创建任意类型的对象,用作规则引擎的输入。我想出的是一个具有多个根的JTree,它与一个属性表(即一个具有2列的JTable)相关联,其中树叶是简单类型(基元,原始包装,日期等)或对象引用。每个引用都指向一个特定的树根,表示稍后要实例化的实际对象。我不记得确切花了多长时间,只需花费几个月的时间来测试和工作。

所以,我不想粉碎你的希望,但是你应该意识到要做这件事需要花费大量的工作。