我正在尝试使用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();
}
}
答案 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)相关联,其中树叶是简单类型(基元,原始包装,日期等)或对象引用。每个引用都指向一个特定的树根,表示稍后要实例化的实际对象。我不记得确切花了多长时间,只需花费几个月的时间来测试和工作。
所以,我不想粉碎你的希望,但是你应该意识到要做这件事需要花费大量的工作。