I have a problem using the invoke method in java.
I have a method to use to provide me a Method object and it looks like:
public static Method provideMethod(String methodName, Class targetClass) throws NoSuchMethodException {
Method method = targetClass.getDeclaredMethod(methodName,null);
//Set accessible provide a way to access private methods too
method.setAccessible(true);
return method;
}
Ok this method works perfectly when I'm trying to access methods, from any context (static, or non static), that have no arguments.
Now the problem is that I can't call invoke and pass arguments to a method that have arguments, for instance:
I have the following method :
private static boolean createDirectory(String path, String fileName) {
...
}
And I want to invoke it like this:
Boolean created = (Boolean) DataUtils.provideMethod("createDirectory", FileUtils.class).
invoke(null, String.class, String.class);
But I'm getting java.lang.NoSuchMethodException: createDirectory [].
Does somebody knows how can I invoke a private static method that have parameters ?
And, how can I pass values to that methods arguments?
Thanks,
Arkde
You're explicitly calling a reflection method which looks for a method declared with the given parameter types - but you're not providing any parameter types.
If you want to find any method with the given name, use getDeclaredMethods() and just filter by name... but then when you call invoke, you need to provide the string values, not the parameter types.
Alternatively, change your provideMethod call to also accept the parameter types, so you can use:
DataUtils.provideMethod("createDirectory", FileUtils.class,
String.class, String.class)
.invoke(null, "foo", "bar")
You're specifically only looking up methods with no arguments when you call
Method method = targetClass.getDeclaredMethod(methodName,null)
In order to find the createDirectory method, you'd need to call
targetClass.getDeclaredMethod("createDirectory", String.class, String.class)
but at present your provideMethod method has no way of doing this.
I would suggest that you change the signature of provideMethod so that it allows the caller to pass in the classes of the arguments that they're looking for, like so:
public static Method provideMethod(String methodName, Class targetClass, Class... parameterTypes) throws NoSuchMethodException {
Method method = targetClass.getDeclaredMethod(methodName, parameterTypes);
//Set accessible provide a way to access private methods too
method.setAccessible(true);
return method;
}
Change this
Method method = targetClass.getDeclaredMethod(methodName, null);
to something like that
Method method = targetClass.getDeclaredMethod(methodName, Class<?>... parameterTypes);
and your provideMethod accordingly.
Related
I have a method that I want to call via reflection:
#Override
public SELF withLogConsumer(Consumer<OutputFrame> consumer) {
this.logConsumers.add(consumer);
return self();
}
Without reflection I would call this method using something like:
Container c = new Container().withLogConsumer(SimpleClass::log);
public void log(OutputFrame frame) {
String msg = frame.getUtf8String();
if (msg.endsWith("\n"))
msg = msg.substring(0, msg.length() - 1);
Log.info(this.clazz, "output", msg);
}
Using reflection I would expect to be able to do:
Constructor<?> ctor = SimpleClass.class.getConstructor();
Object object = ctor.newInstance();
Method withLogConsumer = object.getClass().getMethod("withLogConsumer", Consumer<OutputFrame>.class);
withLogConsumer.invoke(object, SimpleClass::log)
There are two problems with this that I cannot seem to find the answers too:
How do I create a method using reflection that accepts a generic method parameter type? (Like if my method accepted ArrayList)
How do I then pass in a method using double colon syntax?
Ignore the generic type; use the raw, erased, non-generic type. Method withLogConsumer = object.getClass().getMethod("withLogConsumer", Consumer.class);
Explicitly cast the method reference to the appropriate interface type: withLogConsumer.invoke(object, (Consumer<OutputFrame>) SimpleClass::log);
I have a method as follows:
private <T> T _findEntity(Class<T> klass, Object entityId) {
...
}
To invoke this function via reflection, I have tried the below snippet with an unsuccessful result:
Method method = GenericPersistenceManager.class.getDeclaredMethod("_findEntity", Object.class, Object.class);
method.setAccessible(true);
Player player = (Player) method.invoke(genericPersistenceManager, Player.class, "str");
So is there a way to call a method like _findEntity via Java reflection?
Thanks
You are searching for wrong method. Your method takes two arguments of type - Class and Object. And you are searching for method which takes Object as both the arguments.
You should change your 2nd argument to Class.class:
Method method = GenericPersistenceManager.class.getDeclaredMethod("_findEntity",
Class.class, Object.class);
Change your call to
Method method = GenericPersistenceManager.class.getDeclaredMethod("_findEntity", Class.class, Object.class);
When writing something like
doit(43, 44, "hello");
the compiler knows which overloaded method is to be called. When I want to do the same via reflection, I need to find out myself, that the method is
doit(Integer, double, CharSequence...);
and obtain it via something like
Class[] types = {Integer.class, double.class, CharSequence[].class};
declaringClass.getDeclaredMethod("doit", types);
I wonder if there's already something allowing me to write just
Method m = getMethod(declaringClass, "doit", 43, 44, "hello");
I wonder if somebody did this already, as the JLS is a bit complicated in this respect.
Actually, behaving exactly like the compiler is impossible as in Phase 1 the compiler accepts only methods matching without boxing and unboxing. When calling my hypothetical getMethod from above, the distinction between primitives and their wrappers is already lost (because of autoboxing when passing arguments via varargs). This problem seems to have no solution, so let's ignore it.
As suggested in an answer, BeanUtils.invokeMethod comes close. It's supposed to find the best match, whatever it means. Looking at MethodUtils.getMatchingAccessibleMethod shows that
it knows nothing about varargs
it's non-deterministic
so I'm looking for something better.
Alternatively you could use Bean Utils from Apache Commons:
public static Method getAccessibleMethod(
Class clazz,
String methodName,
Class[] parameterTypes)
According documentation:
Return an accessible method (that is, one that can be invoked via
reflection) with given name and parameters. If no such method can be
found, return null. This is just a convenient wrapper for
getAccessibleMethod(Method method).
Parameters:
clazz - get method from this class
methodName - get method with this name
parameterTypes - with these parameters types
The implementation get the accessible method and goes up in the hierarchy until it founds a match to it.
Direct to the Invocation
In order to perform invocation directly as you asked, you could use this method from the same API:
public static Object invokeExactMethod(
Object object,
String methodName,
Object[] args,
Class[] parameterTypes)
throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException
or even
public static Object invokeExactMethod(
Object object,
String methodName,
Object[] args)
throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException
that first locates the method using getAccessibleMethod and later on invokes it.
The MethodHandle is a new way to get a overloaded method using a signature (java 7):
Example:
static class A {
public String get() {
return "A";
}
}
static class B extends A {
public String get() {
return "B";
}
}
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(String.class);
MethodHandle mh = lookup.findVirtual(A.class, "get", mt);;
System.out.println(mh.invoke(new B()));
}
Outputs:
B
i am trying to invoking method using reflect, here i need to pass generic class "clazz" in the arguments.but i am not to achieve that..
here in example i tried using Class but this is not working
public void log( Class<?> clazz,Throwable throwable, String pattern) {
Method method= CommonsLogger.class.getMethod("info", Class<T>,String.class,Throwable.class);
//try catch removed
}
You can't do like this. This should work,
CommonsLogger.class.getMethod("info", String.class, Throwable.class)
I'm trying to make a method that takes an argument of Country.class, User.class etc, and returns argument.count().
All the possible classes that I would give to this method extend from Model and have the method count().
My code:
private static long <T> countModel(Model<T> clazz)
{
// there is other important stuff here, which prevents me from
// simply by-passing the method altogether.
return clazz.count();
}
Called by:
renderArgs.put("countryCount", countModel(Country.class));
However this just doesn't work at all.
How do I do this, please?
I think you want to do
private long countModel(Class<? extends Model> clazz) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
Method countMethod = clazz.getDeclaredMethod("count", null);
return (Long) countMethod.invoke(null, null);
}
Hopefully something like this would work (my reflection skills are not really that good).
Don't fully understand what you are trying to achieve. Did you mean this?
private static long <T> countModel(Model<T> model)
{
return model.count();
}
renderArgs.put("countryCount", countModel(country));
EDIT: If count is a static method, it has nothing to do with model. The static method is not inherited. So all you have to do is to call it directly,
renderArgs.put("countryCount", Country.count());
Clarifying, you want a class (A) that is constrained to have a particular class method (B) and you want to pass that class as an argument to some other method (C) and have that method (C) invoke that class method on that class (A.B())?
The first part, the type constraint, that can't be done. Java's type system just does not work that way.
The second part, passing a class as an argument and invoking a class method on it, that can be done using reflection. This is how to do it, correcting from your code (though you should be more careful with the exceptions than I've been in this).
private static <T extends Model> long countModel(Class<T> clazz) throws Exception
{
return (Long) clazz.getMethod("count").invoke(null);
}
The null is the instance to invoke this on (no instance; it's a class method). The cast to Long is required as the result of invoke() is an Object. The type parameter must go before the result type. And the whole thing can take any class that is a subclass of Model as a parameter; it will just fail at runtime if the count method isn't present. Them's the breaks.
(Also note that if you wanted to pass arguments to count(), you'd have to specify the classes of those arguments to getMethod and the values themselves to invoke, in both cases as subsequent arguments. Both support Java5 variable argument lists.)
In the line
renderArgs.put("countryCount", countModel(Country.class));
you call countModel with a Class<Country>, but you have to call it with an instance of Country like this:
Country country = new Country();
renderArgs.put("countryCount", countModel( country );
In reply to your comment to ZZ Coder; a static method in Java is called in the namespace context of a class, like Model.count() for a static method count() in the class Model, but the method does not become part of Model.class, Model.class is an instance of Class describing the class Model. (I can see where the confusion originates, it would be logical to have a specialised Model.class that includes the static methods, but Java isn't desinged that way.)
Your way out is to use reflection to call the static count() for the class that you pass to your code.
You are not passing an instance of country here, you are passing a Class object:
renderArgs.put("countryCount", countModel(Country.class));
You need to instantiate A model and pass it as an argument:
Model model = new Country();
renderArgs.put("countryCount", countModel(model));
or
Country country = new Country();
renderArgs.put("countryCount", countModel(country));
In this case, Country.class is an object of the Class<Country> type.
You are passing Country.class which is a Class object. How is it a Model object?