If I run the following program:
class Runit{
public static void main(String[] argsWut) throws Exception {
String arg = "what?";
Class[] parameters = { new Object().getClass() };
Object[] args = { arg };
System.out.println("".getClass().getMethod("equals",parameters).invoke("what?",args));
}
};
I get the following on the command line:
true
On the other hand, if I modify the parameters line a little:
class Runit{
public static void main(String[] argsWut) throws Exception {
String arg = "what?";
Class[] parameters = { arg.getClass() }; // changed a little here so it's a bit more dynamic --
Object[] args = { arg };
System.out.println("".getClass().getMethod("equals",parameters).invoke("what?",args));
}
};
I get:
Exception in thread "main" java.lang.NoSuchMethodException: java.lang.String.equals(java.lang.String)
at java.lang.Class.getMethod(Class.java:1605)
at test.Runit.main(Runit.java:7)
From this one example it looks to me as though the getMethod method only works with exact parameters. Is there a way to get some form of a "best fit" method? e.g. If an exact match exists, it would return that method, but if no exact match exists, it can return any method that could accept my given arguments.
You may have better luck with the Apache Commons Lang MethodUtils class, which has a method "invokeMethod" that uses the target arguments for the method to narrow down the appropriate type (i.e., you don't have to tell it the parameter type).
This seems to work:
System.out.println(MethodUtils.invokeMethod("what?", "equals", new Object[] {"what?"}));
See javadocs for more details: http://commons.apache.org/lang/api/org/apache/commons/lang3/reflect/MethodUtils.html#invokeMethod(java.lang.Object,%20java.lang.String,%20java.lang.Object...)
You may be interested in Commons BeanUtils Apache library, specifically this method:
http://commons.apache.org/beanutils/v1.8.0/apidocs/org/apache/commons/beanutils/MethodUtils.html#getMatchingAccessibleMethod%28java.lang.Class,%20java.lang.String,%20java.lang.Class%5b%5d%29
Hope this helps
From the documentation for getMethod():
To find a matching method in a class C: If C declares exactly one
public method with the specified name and exactly the same formal
parameter types, that is the method reflected. If more than one such
method is found in C, and one of these methods has a return type that
is more specific than any of the others, that method is reflected;
otherwise one of the methods is chosen arbitrarily.
(Emphasis mine.)
What you are asking for is to have reflection perform overload resolution for you. And apparently it won't. If you really need this functionality, you can either 1) give up on using reflection and invoke the method directly, or 2) if that's not possible, look up the rules for overload resolution in Java (you could start here), use getMethods() to determine the available methods, and then perform overload resolution manually. Fun times, I know.
Edit: As other answerers have pointed out, someone has already taken the time to do that for you. Cool!
Related
My question, which was similar to this:
Using mockito; is it possible to mock a method that takes a lambda as a parameter and assert variables captured by the lambda?
But different enough that I still had take a while to figure it out was:
How do I verify that a method called by a mock that was used inside a lambda that was passed to a method of another mock object?
This may seem convoluted, but it happens a lot with Java 8 libraries like JDBI, for example, you have a JDBI object:
JDBI MyDBConnection
That you should mock. And then that is used with the withHandle method to pass a lambda implementing the HandleCallback<R,X> type:
//code I'm testing. I implement the lambda, and want to verify it
//calls the correct method in dao provided by JDBI.
MyDBConnection.withHandle(
(handle) -> { ... handle.attach(SomeDao.class).findSomethingInDB(args) .. }
Which is the recommended way to do this.
So I want to verify that findSomethingInDB(eq(args)) is called.
Like I said this was similar, but different enough, that, I at least, will find this answer valuable at some future point, when I forget how to do this. So the original 3rd party library method that invokes my lambda is processed similar to the answer given in the question referenced above, but with some tweaks:
when(JDBIMock.withHandle(any())).then(
//Answer<Void> lambda
invocationOnMock -> {
Object[] args = invocationOnMock.getArguments();
assertEquals(1, args.length);
//the interface def for the callback passed to JDBI
HandleCallback lambda = (HandleCallback) args[0];
when(mockHandle.attach(SomeDao.class)).thenReturn(mockDao);
//this actually invokes my lambda, which implements the JDBI interface, with a mock argument
lambda.withHandle(mockHandle);
//bingo!
verify(mockDao).findSomethingInDB(eq(args));
}
)
See the question, it should be answered sufficiently above ;)
I am trying to do something very similar with verifying the arguments passed to another mock from withHandle on a mock JDBI call in a test.
The answer you give in the question pointed me in the right direction but gives me the error message:
The method then(Answer<?>) in the type OngoingStubbing<Object> is not applicable for the arguments ((<no type> invocationOnMock) -> {})
Instead I had to use a new org.mockito.stubbing.Answer passed to the then, similar to in the other question you linked to.
In your example this would be something like:
when(JDBIMock.withHandle(any())).then(
//Answer<Void> lambda
new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
assertEquals(1, args.length);
//the interface def for the callback passed to JDBI
HandleCallback lambda = (HandleCallback) args[0];
when(mockHandle.attach(SomeDao.class)).thenReturn(mockDao);
//this actually invokes my lambda, which implements the JDBI interface, with a mock argument
lambda.withHandle(mockHandle);
//bingo!
verify(mockDao).findSomethingInDB(eq(args));
return null; // to match the Void type
}
}
)
In my case I was expecting a result list from withHandle so I had to change the Answer type, and return type of answer to match and return a dummy list instead of Void. (The actual results returned didn't matter in this test, only that the expected arguments were passed to my subsequent mock object).
I also moved the verify call outside of the Answer into the main body of my test so it was clearer this was the expectation of the test, not part of the mocking setup.
I'm using reflection discover a method satisfying some conditions and to invoke the found method.
Check following code. Using Groovy..
class TestClass<T>{
T hello(){
return null
}
}
class TestSubClass extends TestClass<List<String>>{
List<String> hello(){
return null
}
}
TestSubClass.methods.each{
if(it.name.contains("hello")){
println it.toGenericString()
}
}
which prints out
public java.util.List<java.lang.String> TestSubClass.hello() // <-- most relevant method for a user of this class
public java.lang.Object TestSubClass.hello()
public java.lang.Object TestSubClass.super$2$hello()
Java reflection is returning multiple declarations of same method based on inheritance/generics, which is understandable.
In my case, I'd like to discover the method with most appropriate signature, including exact type of returnTypes. For example, in the above example, the 1st method in the output has full signature and that's the one we'd usually invoke (without reflection).
Note: above is a simplified example. The real logic is not about finding methods based on naming.
The compiler generates the other 2 methods. Luckily, there is a property that you can check to see this: synthetic:
TestSubClass.declaredMethods.each{
if(it.name.contains("hello") && !it.synthetic) {
println it.toGenericString()
}
}
Which now prints just:
public java.util.List<java.lang.String> test.TestSubClass.hello()
The Java specifications require a method to marked synthetic if it is not explicitly in the source code.
A construct emitted by a Java compiler must be marked as synthetic if
it does not correspond to a construct declared explicitly or
implicitly in source code, unless the emitted construct is a class
initialization method (JVMS ยง2.9).
JAVA specifications
You can try:
TestSubClass.methods.each{
if(it.name.contains("hello") && !m.isSynthetic()){
println it
}
}
You can also check against if the method is bridged. Which is a similar concept:
https://stackoverflow.com/a/5007394/1754020
In my case, I'd like to discover the method with most appropriate
signature, including exact type of return Types.
If it's the Java API that you're wondering about, then you'll want to look at class Class. It contains a large number of reflective methods that allow you to interrogate types.
For example, the following code fragment searches all the methods declared on a supplied type for one method which: takes no arguments, is public and static, and has a return type of DateSerial.Config...
public static <D extends DateSerial<?>> DateSerial.Config<D> obtainMetadata(Class<D> cls) {
Method exe = Stream.of(cls.getDeclaredMethods())
.filter(m -> m.getParameterCount() == 0 &&
m.getReturnType() == DateSerial.Config.class)
.filter(m -> {
int mod = m.getModifiers();
return Modifier.isStatic(mod) && Modifier.isPublic(mod);
})
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"No metadata accessor for " + cls.getName()));
:
:
}
You can get as precise with your interrogations as you need. For example, you can filter methods based on those with a certain number of arguments, the last of which is a String[] array, etc. etc. Caveat emptor: Java reflective code is verbose, ugly, and can be hard to read.
I am trying to call a method, but it is giving this error:
java:112: error:
required: String, String
found: String
reason: actual and formal arguments lists differ in length
Here is the method I'm trying to call:
public void setShippingDest(String inCustName, String inDestn) {
// ...
}
Here is how I'm trying to call it:
shipOrder.setShippingDest("Broome");
Well it's quite simple. Here's the declaration of setShippingDest:
public void setShippingDest(String inCustName, String inDestn)
And here's how you're trying to call it:
shipOrder.setShippingDest("Broome");
You've provided one argument, but there are two parameters? How do you expect that to work? You either need to provide another argument, or remove one parameter.
(I'd also strongly advise that you remove the in prefix from all of your parameters, and look into a real unit testing framework such as JUnit, rather than writing an enormous main method.)
Also if you like to specify only the Customer Name, you could do so by overloading the method as
public void setShippingDest(String inCustName)
{
return setShippingDest(inCustName, defaultvalue1);
}
See how to set default method argument values?
I'm having some trouble using reflection in Java. I'm attempting to save a method of a data structure but getting an error. The error is
java.lang.NoSuchMethodException: cs671.eval.SerialList.add(java.lang.Integer, java.lang.String)
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
structType = "cs671.eval.SerialList", keyType = "java.lang.Integer", and valType = "java.lang.String" are strings that were read in from a file.
Class dataClass = null, comparableClass = null, objectClass = null;
try{ // create data structure
dataClass = Class.forName(structType);
comparableClass = Class.forName(keyType);
objectClass = Class.forName(valType);
}
catch(ClassNotFoundException e){}
java.lang.Object structObj = null;
try{ // Create a data structure object
structObj = dataClass.newInstance();
}
catch(Exception e){}
Method m = null;
try{ // Attempt to get add method for the data structure
m = dataClass.getMethod("add", comparableClass, objectClass); // This is where it fails
}
catch(Exception e){}
Basically I'm trying to get the right method on the right datastructure with the correct classes that are going to get passed into that method but I don't know how to tell the getMethod method that those classes (comparableClass and objectClass) are the correct ones.
Thanks in advance!
Added: Here's the SerialList's add method signature
public void add(java.lang.Comparable, java.lang.Object)
You are saying -
The method, in this case, that I'm trying to get is the add method for a SerialList that takes a Comparable and an Object as its parameters.
But passing the classes - java.lang.Integer, java.lang.String.
Just a note - Only public methods are visible to getMethod() for non-publics you would have to use getDeclaredMethod() instead.
From http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getMethod%28java.lang.String,%20java.lang.Class...%29:
To find a matching method in a class C: If C declares exactly one public method with the specified name and exactly the same formal parameter types, that is the method reflected. If more than one such method is found in C, and one of these methods has a return type that is more specific than any of the others, that method is reflected; otherwise one of the methods is chosen arbitrarily.
=> You need to pass java.lang.Comparable.class & java.lang.Object.class
Apologies for providing wrong answer earlier. Based on your comments it appears that you're trying to get a Method by avoiding to provide specific parameter types needed in the signature of that method.
If my understanding is correct then you should rather use Class#getMethods() and examine the returned Method[] for your method. Consider a skeleton code like this:
Method[] methods = dataClass.getMethods();
boolean matched = false;
// find a matching method by name
for (Method method: methods) {
Class<?>[] parameterTypes = method.getParameterTypes();
if ( "add".equals(method.getName()) && parameterTypes.length == 2 ) {
// method is your target method
// however you'll need more strict checks if there can be another add method
// in your class with 2 different parameter types
}
}
As other answers have stated, to use getMethod() you need to know and use the actual declared formal parameters of the method you are attempting to retrieve.
However, if for some reason you do not know the formal parameters at compile time, then you can iterate over all of the methods from the class until you find a method that fits your parameters (or find the most specific method that fits your parameters).
There is functionality written to do this already in apache commons bean utils, specifically in org.apache.commons.beanutils.MethodUtils.invokeMethod(...) and MethodUtils.getMatchingAccessibleMethod(...).
Source code for the above methods can be easily viewed online here.
I got a strange problem with a call to a Java method from JRuby.
In my Java class these methods are defined twice, and it appears JRuby calls the wrong one.
So I tried to use java_method, but I always got a:
TypeError: cannot convert instance of class org.jruby.RubyModule to class java.lang.Class
Here's my Java code:
public class Renderer {
...
public void addRenderer(IElementRenderer r) {
System.out.println("Added element render: " + r.getClass().toString());
basicRenderers.add(r);
rendererMap.put(r.elementClass(), r);
}
public void addRenderer(IBasicRenderer r) {
System.out.println("SHOULD NOT GO THERE !!");
basicRenderers.add(r);
}
}
and my JRuby code:
add_renderer = renderer.java_method :add_renderer, [Java::dragon.render.IElementRenderer]
add_renderer.call TextRenderer.new
I also tried with java_send but I got the same error:
renderer.java_send(:add_renderer, [Java::dragon.render.IElementRenderer], TextRenderer.new)
Next, I tried with:
renderer.add_renderer(TextRenderer.new.to_java(IElementRenderer))
This time no errors but the wrong method is called ...
How can I fix this problem?
You can fix that cannot convert instance of class org.jruby.RubyModule to class java.lang.Class using java.lang.Class.for_name
In your case, it is
add_renderer = renderer.java_method :add_renderer, [java.lang.Class.for_name("dragon.render.IElementRenderer")]
This is because java interfaces become Ruby Modules by default and the second argument to :java_method expects an array of Class objects.
You can print the matched method to see it is matching the intended method.
For example, I see below code is matching the println(String) on System.out.
>>java.lang.System.out.java_method "println", [java.lang.Class.for_name("java.lang.String")]
#<Method: Java::JavaIo::PrintStream#(java.lang.String)>
I've had problems like this before. It was many versions ago and I think JRuby's method matching algorithm has improvedd over time. Are you using the latest JRuby?
If nothing else works, you may need to add another method, or a wrapper class. Something that distinguishes your methods by name or number of parameters, not just parameter type.