I have a question about reflection
I am trying to have some kind of eval() method. So i can call for example:
eval("test('woohoo')");
Now I understand the there is no eval method in java but there is reflection. I made the following code:
String s = "test";
Class cl = Class.forName("Main");
Method method = cl.getMethod(s, String.class);
method.invoke(null, "woohoo");
This works perfectly (of course there is a try, catch block around this code). It runs the test method. However I want to call multiple methods who all have different parameters.
I don't know what parameters these are (so not only String.class). But how is this possible? how
can I get the parameter types of a method ?
I know of the following method:
Class[] parameterTypes = method.getParameterTypes();
But that will return the parameterTypes of the method I just selected! with the following statement:
Method method = cl.getMethod(s, String.class);
Any help would be appreciated !
You will need to call Class.getMethods() and iterate through them looking for the correct function.
For (Method method : clazz.getMethods()) {
if (method.getName().equals("...")) {
...
}
}
The reason for this is that there can be multiple methods with the same name and different parameter types (ie the method name is overloaded).
getMethods() returns all the public methods in the class, including those from superclasses. An alternative is Class.getDeclaredMethods(), which returns all methods in that class only.
You can loop over all methods of a class using:
cls.getMethods(); // gets all public methods (from the whole class hierarchy)
or
cls.getDeclaredMethods(); // get all methods declared by this class
.
for (Method method : cls.getMethods()) {
// make your checks and calls here
}
You can use getMethods() which returns an array of all methods of a class.
Inside the loop you can inspect the parameters of each method.
for(Method m : cl.getMethods()) {
Class<?>[] params = m.getParameterTypes();
...
}
Otherwise you can use getDelcaredMethods() which will allow you to "see" private methods (but not inherited methods). Note that if you want to invoke a private methods you must first apply setAccessible(boolean flag) on it:
for(Method m : cl.getDelcaredMethods()) {
m.setAccessible(true);
Class<?>[] params = m.getParameterTypes();
...
}
Ok thanxs to all the people who answered my question here the final solution:
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args){
String func = "test";
Object arguments[] = {"this is ", "really cool"};
try{
Class cl = Class.forName("Main");
for (Method method : cl.getMethods()){
if(method.getName().equals(func)){
method.invoke(null, arguments);
}
}
} catch (Exception ioe){
System.out.println(ioe);
}
}
public static void test(String s, String b){
System.out.println(s+b);
}
}
Related
I'm trying to override a private method on a Java class using meta programming. The code looks something like this:
// Java class
public class MyClass{
private ClassOfSomeSort property1;
private ClassOfSomeOtherSort property2;
public void init(){
property1 = new ClassOfSomeSort();
property2 = new ClassOfSomeOtherSort();
doSomethingCrazyExpensive();
}
private void doSomethingCrazyExpensive(){
System.out.println("I'm doing something crazy expensive");
}
}
// Groovy class
public class MyClassTest extends Specification{
def "MyClass instance gets initialised correctly"(){
given:
ExpandoMetaClass emc = new ExpandoMetaClass( MyClass, false )
emc.doSomethingCrazyExpensive = { println "Nothing to see here..." }
emc.initialize()
def proxy = new groovy.util.Proxy().wrap( new MyClass() )
proxy.setMetaClass( emc )
when:
proxy.init()
then:
proxy.property1 != null
proxy.property2 != null
}
}
The problem is that the overridden implementation of doSomethingCrazyExpensive isn't called - I think that this is because the private method is called by the init() method internally and not called through the metaClass. If I call myProxy.doSomethingCrazyExpensive() directly, the overridden method is invoked, so the meta-programming does work to some degree.
Is there a way to use meta programming to override a method on a Java class (or instance) in such a way that the overridden implementation is called when it is invoked internally?
Groovy as operator is quite powerful, and can create proxies out of concrete types whose changes are visible in Java. Sadly, seems like it can't override private methods, though i managed to change a public method:
Java class:
public class MyClass{
public void init(){
echo();
doSomethingCrazyExpensive();
}
public void echo() { System.out.println("echo"); }
private void doSomethingCrazyExpensive(){
System.out.println("I'm doing something crazy expensive");
}
}
Groovy test:
class MyClassTest extends GroovyTestCase {
void "test MyClass instance gets initialised correctly"(){
def mock = [
doSomethingCrazyExpensive: { println 'proxy crazy' },
echo: { println 'proxy echo' }
] as MyClass
mock.init()
mock.doSomethingCrazyExpensive()
}
}
It prints:
proxy echo
I'm doing something crazy expensive
proxy crazy
So the public method got intercepted and changed, even when being called from Java, but not the private one.
You cannot override a method called from Java code in Groovy using metaClass.
That's why you won't be able to "mock" the call to this private method in Java: it is being called by the Java class itself, not from Groovy.
This limitation wouldn't apply, of course, if your class was written in Groovy.
I would suggest that you refactor the Java class if you can so that you can use normal means to mock the expensive method call. Or even make the method protected, then override it in a sub-class.
I stumbled on this question and thought I should provide a different answer: Yes you can override an existing method - you just have to change the meta class to ExpandoMetaClass.
This happens automatically when you add your first method, for example.
Here's an example:
println ""
class Bob {
String name
String foo() { "foo" }
void print() { println "$name = ${foo()} ${fum()} metaclass=${Bob.metaClass}"}
def methodMissing(String name, args) { "[No method ${name}]" }
}
new Bob(name:"First ").print()
Bob.metaClass.fum = {-> "fum"}
new Bob(name:"Second").print()
Bob.metaClass.fum = {-> "fum"}
new Bob(name:"Third ").print()
Bob.metaClass.foo = {-> "Overriden Foo"}
new Bob(name:"Fourth").print()
The results are:
First = foo [No method fum] metaclass=org.codehaus.groovy.runtime.HandleMetaClass#642a7222[groovy.lang.MetaClassImpl#642a7222[class Bob]]
Second = foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
Third = foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
Fourth = Overriden Foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
You can see after the fum method was added the meta class changed to an expando. Now when the attempt is made to override the original foo - it works.
It seems you can't use Groovy metaprogramming to replace methods of Java classes - even public methods - try the following in the Groovy console to confirm:
ArrayList.metaClass.remove = { obj ->
throw new Exception('remove')
}
ArrayList.metaClass.remove2 = { obj ->
throw new Exception('remove2')
}
def a = new ArrayList()
a.add('it')
// returns true because the remove method defined by ArrayList is called,
// i.e. our attempt at replacing it above has no effect
assert a.remove('it')
// throws an Exception because ArrayList does not define a method named remove2,
// so the method we add above via the metaClass is invoked
a.remove2('it')
If you can modify the source code of MyClass, I would either make doSomethingCrazyExpensive protected, or preferably, refactor it so that it's more test-friendly
public class MyClass {
private ClassOfSomeSort property1;
private ClassOfSomeOtherSort property2;
private CrazyExpensive crazyExpensive;
public MyClass(CrazyExpensive crazyExpensive) {
this.crazyExpensive = crazyExpensive;
}
public void init(){
property1 = new ClassOfSomeSort();
property2 = new ClassOfSomeOtherSort();
crazyExpensive.doSomethingCrazyExpensive();
}
}
public interface CrazyExpensive {
public void doSomethingCrazyExpensive();
}
After making the changes above, when testing MyClass you can easily instantiate it with a mock/stub implementation of CrazyExpensive.
I'm trying to override a private method on a Java class using meta programming. The code looks something like this:
// Java class
public class MyClass{
private ClassOfSomeSort property1;
private ClassOfSomeOtherSort property2;
public void init(){
property1 = new ClassOfSomeSort();
property2 = new ClassOfSomeOtherSort();
doSomethingCrazyExpensive();
}
private void doSomethingCrazyExpensive(){
System.out.println("I'm doing something crazy expensive");
}
}
// Groovy class
public class MyClassTest extends Specification{
def "MyClass instance gets initialised correctly"(){
given:
ExpandoMetaClass emc = new ExpandoMetaClass( MyClass, false )
emc.doSomethingCrazyExpensive = { println "Nothing to see here..." }
emc.initialize()
def proxy = new groovy.util.Proxy().wrap( new MyClass() )
proxy.setMetaClass( emc )
when:
proxy.init()
then:
proxy.property1 != null
proxy.property2 != null
}
}
The problem is that the overridden implementation of doSomethingCrazyExpensive isn't called - I think that this is because the private method is called by the init() method internally and not called through the metaClass. If I call myProxy.doSomethingCrazyExpensive() directly, the overridden method is invoked, so the meta-programming does work to some degree.
Is there a way to use meta programming to override a method on a Java class (or instance) in such a way that the overridden implementation is called when it is invoked internally?
Groovy as operator is quite powerful, and can create proxies out of concrete types whose changes are visible in Java. Sadly, seems like it can't override private methods, though i managed to change a public method:
Java class:
public class MyClass{
public void init(){
echo();
doSomethingCrazyExpensive();
}
public void echo() { System.out.println("echo"); }
private void doSomethingCrazyExpensive(){
System.out.println("I'm doing something crazy expensive");
}
}
Groovy test:
class MyClassTest extends GroovyTestCase {
void "test MyClass instance gets initialised correctly"(){
def mock = [
doSomethingCrazyExpensive: { println 'proxy crazy' },
echo: { println 'proxy echo' }
] as MyClass
mock.init()
mock.doSomethingCrazyExpensive()
}
}
It prints:
proxy echo
I'm doing something crazy expensive
proxy crazy
So the public method got intercepted and changed, even when being called from Java, but not the private one.
You cannot override a method called from Java code in Groovy using metaClass.
That's why you won't be able to "mock" the call to this private method in Java: it is being called by the Java class itself, not from Groovy.
This limitation wouldn't apply, of course, if your class was written in Groovy.
I would suggest that you refactor the Java class if you can so that you can use normal means to mock the expensive method call. Or even make the method protected, then override it in a sub-class.
I stumbled on this question and thought I should provide a different answer: Yes you can override an existing method - you just have to change the meta class to ExpandoMetaClass.
This happens automatically when you add your first method, for example.
Here's an example:
println ""
class Bob {
String name
String foo() { "foo" }
void print() { println "$name = ${foo()} ${fum()} metaclass=${Bob.metaClass}"}
def methodMissing(String name, args) { "[No method ${name}]" }
}
new Bob(name:"First ").print()
Bob.metaClass.fum = {-> "fum"}
new Bob(name:"Second").print()
Bob.metaClass.fum = {-> "fum"}
new Bob(name:"Third ").print()
Bob.metaClass.foo = {-> "Overriden Foo"}
new Bob(name:"Fourth").print()
The results are:
First = foo [No method fum] metaclass=org.codehaus.groovy.runtime.HandleMetaClass#642a7222[groovy.lang.MetaClassImpl#642a7222[class Bob]]
Second = foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
Third = foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
Fourth = Overriden Foo fum metaclass=groovy.lang.ExpandoMetaClass#21be3395[class Bob]
You can see after the fum method was added the meta class changed to an expando. Now when the attempt is made to override the original foo - it works.
It seems you can't use Groovy metaprogramming to replace methods of Java classes - even public methods - try the following in the Groovy console to confirm:
ArrayList.metaClass.remove = { obj ->
throw new Exception('remove')
}
ArrayList.metaClass.remove2 = { obj ->
throw new Exception('remove2')
}
def a = new ArrayList()
a.add('it')
// returns true because the remove method defined by ArrayList is called,
// i.e. our attempt at replacing it above has no effect
assert a.remove('it')
// throws an Exception because ArrayList does not define a method named remove2,
// so the method we add above via the metaClass is invoked
a.remove2('it')
If you can modify the source code of MyClass, I would either make doSomethingCrazyExpensive protected, or preferably, refactor it so that it's more test-friendly
public class MyClass {
private ClassOfSomeSort property1;
private ClassOfSomeOtherSort property2;
private CrazyExpensive crazyExpensive;
public MyClass(CrazyExpensive crazyExpensive) {
this.crazyExpensive = crazyExpensive;
}
public void init(){
property1 = new ClassOfSomeSort();
property2 = new ClassOfSomeOtherSort();
crazyExpensive.doSomethingCrazyExpensive();
}
}
public interface CrazyExpensive {
public void doSomethingCrazyExpensive();
}
After making the changes above, when testing MyClass you can easily instantiate it with a mock/stub implementation of CrazyExpensive.
i am trying to call a private method using java reflection i developed a small method to call the others methods iterating over all methods and comparing by name and by parameters type i have invoke 4 methods with success.
i have one question.
1). is this the best way to do it?. because i understand class.getMethod only match public methods. Java have something built-in?
here is my code.
public class Compute
{
private Method getMethod(Class clazz,String methodName,Class...parametersClass)
{
outer:
Method[] methods = clazz.getDeclaredMethods();
for(Method method:methods)
{
//comparing the method types... of the requested method and the real method.....
if(method.getName().equals(methodName) && parametersClass.length== method.getParameterTypes().length)//it a possible match
{
Class[]arrayForRealParameters = method.getParameterTypes();
//comparing the method types... of the requested method and the real method.....
for(int i=0;i<arrayForRealParameters.length;i++)if(!arrayForRealParameters[i].getSimpleName().equals(parametersClass[i].getSimpleName()))continue outer;
return method;
}
}
return null;
}
private Calendar getCalendar(){return java.util.Calendar.getInstance();}
private void methodByReflex(){System.out.println("Empty Parameters.");}
private void methodByReflex(Integer a,Integer b){System.out.println(a*b);}
private void methodByReflex(String a,String b){System.out.println(a.concat(b));}
public static void main(String[] args) throws Exception
{
Compute clazz = new Compute();
Class[]arrayOfEmptyClasses=new Class[]{};
Class[]arrayOfIntegerClasses=new Class[]{Integer.class,Integer.class};
Class[]arrayOfStringClasses=new Class[]{String.class,String.class};
Method calendarMethod=clazz.getMethod(clazz.getClass(),"getCalendar",arrayOfEmptyClasses);
Method emptyMethod=clazz.getMethod(clazz.getClass(),"methodByReflex",arrayOfEmptyClasses);
Method intMethod=clazz.getMethod(clazz.getClass(),"methodByReflex",arrayOfIntegerClasses);
Method stringMethod=clazz.getMethod(clazz.getClass(),"methodByReflex",arrayOfStringClasses);
System.out.println((calendarMethod==null)+" "+(emptyMethod==null)+" "+(intMethod==null)+" "+(stringMethod==null));//prints false false false false
Calendar cal = (Calendar)calendarMethod.invoke(clazz);
System.out.println(cal.getTime());//prints current time
stringMethod.invoke(clazz,"John ","Lennon");//Prints John Lennon
emptyMethod.invoke(clazz);//prints Empty Parameters.
intMethod.invoke(clazz,13,13);// prints 169
Integer a=10,b=10;
intMethod.invoke(clazz,a,b);//prints 100
}
}
any help is appreciate it.
thanks a lot.
You are getting a list of all the methods on a class with getDeclaredMethods. Try using the getDeclaredMethod (singular) method, which takes as a parameter the method name and a varargs argument for the classes of the parameter types. That should get you directly to the method so you don't have to iterate through all methods yourself.
Example:
clazz.getDeclaredMethod(methodName, parametersClass);
See how i invoke private method with parameters using java reflection API-
Here i have written a method named as executePrivateMethod like :
public Object executePrivateMethod(Class<?> clazz,String methodName,Class<?>[] parameterTypes,Object ... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, SecurityException, NoSuchMethodException{
//get declared Method for execution
Method pvtMethod = clazz.getDeclaredMethod(methodName,parameterTypes);
pvtMethod.setAccessible(true);
//invoke loadConfiguration() method and return result Object
return pvtMethod.invoke(clazz.newInstance(),args);
}
How to call:
//params
private Map<String, String> requestParams = new HashMap<String, String>();
requestParams.put("a","a");
requestParams.put("b","b");
requestParams.put("c","c");
requestParams.put("d","d");
//call
executePrivateMethod(JSGeneratorFacade.class,"testMethod",new Class<?>[]{Map.class},requestParams);
I'm having problems invoking non-static methods through reflection. My code is below. When I try to do "ClassnameRemoved.printMessageToLogger(Level.INFO, "Test");", I get "Could not find method 'log' in class Logger. This is a normal Java class, so you are probably using a modified/outdata Java version.". Thanks in advance!
private static void printMessageToLogger(Level lvl, String message) {
try{
Class<?> clazz = Class.forName("net.packgeName.omitted.Main");
Field logger = clazz.getDeclaredField("tcLog");
Method logMethod = logger.getDeclaringClass().getDeclaredMethod("log", Level.class, String.class);
logMethod.invoke(logger, lvl, message);
}
// catch methods omitted to save space
}
If the method is not static, you need an instance of the class.
Look at this example:
Class classDefinition = Class.forName(className);
object = classDefinition.newInstance();
Here is a piece of code I have: (what I am trying to do with it is: define a method "renamingrule" in my main class, instantiate a instance of my other class "renamescript" and call its rename method passing as a parameter the "renamingrule" method i've defined in the main class. Everything is well in the RenamScript class, no errors, but i dont know how to call the rename method of the script class from my main class/method. thanks)
public class RenameScript2 {
...
public void rename(Method methodToCall) throws IOException, IllegalAccessException, InvocationTargetException {
try
{
...
String command = "cmd /c rename "+_path+"\\"+"\""+next_file+"\" "
+"\""+methodToCall.invoke(next_file, next_index)+"\"";
p = Runtime.getRuntime().exec(command);
}catch(IOException e1) {} catch(IllegalAccessException IA1) {} catch(InvocationTargetException IT1) {} ;
}//end of rename
} //end of class
//=======================================
public class RenameScriptMain2 {
public static String RenamingRule(String input, int file_row)
{
String output = "renamed file "+(file_row+1)+".mp3";
return output;
}
public static void main(String[] args) throws IOException
{
RenameScript2 renamer = new RenameScript2();
renamer.setPath("c:\\users\\roise\\documents\\netbeansprojects\\temp\\files");
try{
renamer.rename(RenamingRule);
}catch(IOException e2) {};
System.out.println("Done from main()\n\n");
}
} //end of class
You get hold of the Method object through Class.getMethod method. Something like this:
RenameScript2.class.getMethod("rename", parameters);
However, I suggest you consider writing an interface for a class that can perform the renaming, instead of passing a Method.
Such interface could look like
interface RenameAction {
void performRename();
}
To wrap the script in a RenameAction object you would do something like
RenameAction action = new RenameAction() {
void performRename() {
// ...
String command = "cmd /c rename "+_path+"\\"+"\""+next_file+"\" "...
p = Runtime.getRuntime().exec(command);
// ...
}
};
You would then simply do like this:
public void rename(RenameAction action) {
action.performRename();
}
Firstly, aioobe is definitely correct, passing a Method object is a little ugly. I'll assume that you're stuck with it!
To get a method, you'll need to use reflection. The below code grabs the method called toString on the class Integer. It then invokes the toString method.
Method method = Integer.class.getMethod("toString");
Object o = method.invoke(new Integer(7));
System.out.println(o);
Static methods don't need to pass the first parameter to method.invoke
Method method = File.class.getMethod("listRoots");
System.out.println(method.invoke(null));
This shows the reason why you shouldn't use it. That string "toString" and "listRoots" are not refactorable. If someone renames a method, then instead of a compile-time error, you'll get a runtime exception thrown (hence the exceptions you'll need to catch, NoSuchMethodException and IllegalAccessException). It's also much slower to use reflection than to use normal code.
Here is how you should do:
Make class RenameScript2 abstract by adding an abstract method public static String RenamingRule(String input, int file_row)
Then have your main class RenameScriptMain2 extend above class RenameScript2 and provide implementation of the method RenamingRule().
Now inside main method create instance of the class RenameScriptMain2 and call method RenamingRule()