作为开发小型 ScriptEngine 的一部分,我反思性地调用 java 方法。脚本引擎的调用为我提供了对象的方法名称和参数数组。为了调用该方法,我尝试通过调用 Class.getMethod(name, argument types) 来解决它。
然而,只有当参数的类和方法期望的类相同时,这才有效。
Object o1 = new Object();
Object out = System.out;
//Works as System.out.println(Object) is defined
Method ms = out.getClass().getMethod("println",o1.getClass());
Object o2 = new Integer(4);
//Does not work as System.out.println(Integer) is not defined
Method mo = out.getClass().getMethod("println",o2.getClass());
我想知道是否有一种“简单”的方法来获得正确的方法,如果可能的话,最适合参数类型,或者我是否必须自己实现。
最接近的是:
Object o1 = new Integer(1);
Object o2 = new String("");
getMethod(name, o1.getClass())//println(Object)
getMethod(name, o2.getClass())//println(String)
更新:
为了澄清我需要什么:
脚本引擎是我在空闲时间编写的一个小项目,因此没有必须遵循的严格规则。所以我认为选择从引擎调用的方法就像java编译器在编译时选择仅使用动态类型而不是对象的静态类型的方法一样。(有或没有自动装箱)
这就是我首先希望 Class.getMethod() 能够解决的问题。但是 Class.getMethod() 需要与 Method 声明的参数类型完全相同的类,使用子类将导致 no such method 异常。发生这种情况可能有充分的理由,但使该方法对我来说毫无用处,因为我事先不知道哪些参数类型适合。
另一种方法是调用 Class.getMethods() 并迭代返回的数组并尝试找到合适的方法。然而,如果我不想采用我遇到的第一个“好”方法,那么这会很复杂,所以我希望有一个现有的解决方案至少可以处理:
- 最适合:If arg.getClass() = =
子类和方法 m(超类),
m(Subclass) 然后调用 m(Subclass)
- 变量参数:
System.out.printf(String ,String...)
支持自动装箱也很好。
如果调用无法解析,则可能会抛出异常 ( ma(String,Object), ma(Object, String), args= String,String)
(如果您做到了这里,感谢您花时间阅读它:-))
As part of developing a small ScriptEngine, I reflectively call java methods. A call by the script engine gives me the object the method name and an array of arguments. To call the method I tried to resolve it with a call to Class.getMethod(name, argument types).
This however only works when the classes of the arguments and the classes expected by the Method are the same.
Object o1 = new Object();
Object out = System.out;
//Works as System.out.println(Object) is defined
Method ms = out.getClass().getMethod("println",o1.getClass());
Object o2 = new Integer(4);
//Does not work as System.out.println(Integer) is not defined
Method mo = out.getClass().getMethod("println",o2.getClass());
I would like to know if there is a "simple" way to get the right method, if possible with the closest fit for the argument types, or if I have to implement this myself.
Closest fit would be:
Object o1 = new Integer(1);
Object o2 = new String("");
getMethod(name, o1.getClass())//println(Object)
getMethod(name, o2.getClass())//println(String)
Update:
To clarify what I need:
The Script Engine is a small project I write in my free time so there are no strikt rules I have to follow. So I thought that selecting methods called from the Engine the same way the java compiler selects methods at compile time only with the dynamic type and not the static type of the Object would work.(with or without autoboxing)
This is what I first hoped that the Class.getMethod() would solve. But the Class.getMethod() requires the exact same Classes as argument types as the Method declares, using a subclass will result in a no such method Exception. This may happen for good reasons, but makes the method useless for me, as I don't know in advance which argument types would fit.
An alternate would be to call Class.getMethods() and iterate through the returned array and try to find a fitting method. This would however be complicated if I don't just want to take the first "good" method which I come across, so I hoped that there would be an existing solution which at least handles:
- closest fit: If arg.getClass() ==
subclass and methods m(Superclass),
m(Subclass) then call m(Subclass)
- variable arguments:
System.out.printf(String ,String...)
Support for autoboxing would be nice, too.
If a call cannot be resolved it may throw an exception ( ma(String,Object), ma(Object, String), args= String,String)
(If you made it till here, thanks for taking the time to read it:-))
发布评论
评论(3)
正如其他人指出的那样,没有标准方法可以执行此操作,因此您将必须实现自己的重载解析算法。
尽可能严格地遵循 javac 的重载解析规则可能是有意义的:
http://java.sun.com/docs/ books/jls/third_edition/html/expressions.html#292575
您可能可以忽略动态类型脚本语言的泛型,但您仍然可以从 编译器自动生成的桥接方法。
需要注意的一些陷阱:
Class.isAssignableFrom
不知道自动扩展基元转换,因为这些是在编译器中实现的语法糖;它们不会出现在 VM 或类层次结构中。例如,int.class.isAssignableFrom(short.class)
返回false
。Class.isAssignableFrom
也不知道自动装箱。Integer.class.isAssignableFrom(int.class)
返回false
。Class.isInstance
和Class.cast
将Object
作为参数;您不能将原始值传递给它们。它们还返回一个Object
,因此它们不能用于拆箱((int) new Integer(42)
在 Java 源代码中是合法的,但int.class.cast (new Integer(42))
抛出异常。)As others have pointed out there is no standard method that does this, so you are going to have to implement your own overload resolution algorithm.
It would probably make sense to follow javac's overload resolution rules as closely as possible:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575
You can probably ignore generics for a dynamically-typed scripting language, but you might still benefit from the bridge methods that the compiler generates automatically.
Some pitfalls to watch out for:
Class.isAssignableFrom
does not know about automatic widening primitive conversions, because these are syntactic sugar implemented in the compiler; They do not occur in the VM or class hierarchy. e.g.int.class.isAssignableFrom(short.class)
returnsfalse
.Class.isAssignableFrom
does not know about auto-boxing.Integer.class.isAssignableFrom(int.class)
returnsfalse
.Class.isInstance
andClass.cast
take anObject
as an argument; You cannot pass primitive values to them. They also return anObject
, so they cannot be used for unboxing ((int) new Integer(42)
is legal in Java source butint.class.cast(new Integer(42))
throws an exception.)我建议您使用
getMethods()
。它返回所有公共方法的数组 (Method[]
)。这里最重要的是:
“如果类声明了多个具有相同参数类型的公共成员方法,则它们都包含在返回的数组中。”
然后您需要做的是使用结果 在此数组中确定其中哪一个(如果有)最接近匹配。由于最接近的匹配很大程度上取决于您的要求和特定应用程序,因此您自己编写代码确实有意义。
示例代码说明了如何执行此操作的一种方法:
在此示例中,
getMethod(String, Class)
方法将返回一种方法,该方法仅具有一个参数,即 < code>int、float
、double
或Number
的超类。这是一个基本的实现 - 它返回第一个符合要求的方法。您需要扩展它以创建所有匹配方法的列表,然后根据某种标准对它们进行排序,并返回最佳匹配方法。
然后,您可以更进一步,创建更通用的
getMethod(String, Class)
方法,以处理更多可能的“紧密匹配”场景,甚至可能不止一个参数HTH
编辑:正如 @finnw 所指出的,使用
Class#isAssignableFrom(Class cls)
,由于其局限性,正如我在示例代码中所测试的原语与Number
对象分开。I would suggest that you use
getMethods()
. It returns an array of all public methods (Method[]
).The most important thing here is:
"If the class declares multiple public member methods with the same parameter types, they are all included in the returned array."
What you will then need to do is to use the results in this array to determine which one of them (if any) are the closest match. Since what the closest match should be depends very much on your requirements and specific application, it does make sense to code it yourself.
Sample code illustrating one approach of how you might go about doing this:
In this example, the
getMethod(String, Class<?>)
method will return a method that with only one parameter which is anint
,float
,double
, or a superclass ofNumber
.This is a rudimentary implementation - It returns the first method that fits the bill. You would need to extend it to create a list of all methods that match, and then sort them according to some sort of criteria, and return the best matching method.
You can then take it even further by creating the more general
getMethod(String, Class<?>)
method, to handle more of the possible "close match" scenarios, and possibly even more than one paramterHTH
Edit: As @finnw has pointed out, be careful when using
Class#isAssignableFrom(Class<?> cls)
, due to its limitations, as I have in my sample code, testing the primitives separately from theNumber
objects.AFAIK,没有简单的方法可以做到这种事情。当然,标准 Java 类库中没有任何东西可以做到这一点。
问题是没有单一的“正确”答案。您需要考虑所有用例,决定“正确的方法”应该是什么,并相应地实现您的反射代码。
AFAIK, there is no simple way to do this kind of thing. Certainly, there's nothing in the standard Java class libraries to do this.
The problem is that there is no single "right" answer. You need to consider all of your use-cases, decide what the "right method" should be and implement your reflection code accordingly.