Come invoco un metodo Java quando si specifica il nome del metodo come una stringa?



Answers

Utilizzare l' invocazione del metodo dalla riflessione:

Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod("method name", parameterTypes);
method.invoke(objectToInvokeOn, params);

Dove:

  • "class name" è il nome della classe
  • objectToInvokeOn è di tipo Object ed è l'oggetto su cui si desidera richiamare il metodo
  • "method name" è il nome del metodo che si desidera chiamare
  • parameterTypes è di tipo Class[] e dichiara i parametri che il metodo assume
  • params è di tipo Object[] e dichiara i parametri da passare al metodo
Question

Se ho due variabili:

Object obj;
String methodName = "getName";

Senza conoscere la classe di obj , come posso chiamare il metodo identificato da methodName su di esso?

Il metodo chiamato non ha parametri e un valore di restituzione String . È un getter per un bean Java .




Questo sta funzionando bene per me:

public class MethodInvokerClass {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, ClassNotFoundException, InvocationTargetException, InstantiationException {
        Class c = Class.forName(MethodInvokerClass.class.getName());
        Object o = c.newInstance();
        Class[] paramTypes = new Class[1];
        paramTypes[0]=String.class;
        String methodName = "countWord";
         Method m = c.getDeclaredMethod(methodName, paramTypes);
         m.invoke(o, "testparam");
}
public void countWord(String input){
    System.out.println("My input "+input);
}

}

Produzione:

My input testparam

Sono in grado di invocare il metodo passando il suo nome ad un altro metodo (come main).




Per completare le risposte del mio collega, potresti voler prestare molta attenzione a:

  • chiamate statiche o di istanza (in un caso, non è necessaria un'istanza della classe, nell'altra, potrebbe essere necessario fare affidamento su un costruttore predefinito esistente che potrebbe o meno esserci)
  • chiamata al metodo pubblico o non pubblico (per quest'ultimo, è necessario chiamare setAccessible sul metodo all'interno di un blocco doPrivileged , altri findbug non saranno felici )
  • incapsulando in un'eccezione applicativa più gestibile se si desidera rigettare le numerose eccezioni di java system (da qui il CCException nel codice sottostante)

Ecco un vecchio codice java1.4 che tiene conto di questi punti:

/**
 * Allow for instance call, avoiding certain class circular dependencies. <br />
 * Calls even private method if java Security allows it.
 * @param aninstance instance on which method is invoked (if null, static call)
 * @param classname name of the class containing the method 
 * (can be null - ignored, actually - if instance if provided, must be provided if static call)
 * @param amethodname name of the method to invoke
 * @param parameterTypes array of Classes
 * @param parameters array of Object
 * @return resulting Object
 * @throws CCException if any problem
 */
public static Object reflectionCall(final Object aninstance, final String classname, final String amethodname, final Class[] parameterTypes, final Object[] parameters) throws CCException
{
    Object res;// = null;
    try {
        Class aclass;// = null;
        if(aninstance == null)
        {
            aclass = Class.forName(classname);
        }
        else
        {
            aclass = aninstance.getClass();
        }
        //Class[] parameterTypes = new Class[]{String[].class};
    final Method amethod = aclass.getDeclaredMethod(amethodname, parameterTypes);
        AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
                amethod.setAccessible(true);
                return null; // nothing to return
            }
        });
        res = amethod.invoke(aninstance, parameters);
    } catch (final ClassNotFoundException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+CLASS, e);
    } catch (final SecurityException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_SECURITY_ISSUE, e);
    } catch (final NoSuchMethodException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_NOT_FOUND, e);
    } catch (final IllegalArgumentException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ILLEGAL_ARGUMENTS+String.valueOf(parameters)+GenericConstants.CLOSING_ROUND_BRACKET, e);
    } catch (final IllegalAccessException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ACCESS_RESTRICTION, e);
    } catch (final InvocationTargetException e) {
    throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_INVOCATION_ISSUE, e);
    } 
    return res;
}



Il metodo può essere invocato in questo modo. Ci sono anche più possibilità (controlla l'api di riflessione), ma questa è la più semplice:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Assert;
import org.junit.Test;

public class ReflectionTest {

    private String methodName = "length";
    private String valueObject = "Some object";

    @Test
    public void testGetMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            IllegalAccessException, InvocationTargetException {
        Method m = valueObject.getClass().getMethod(methodName, new Class[] {});
        Object ret = m.invoke(valueObject, new Object[] {});
        Assert.assertEquals(11, ret);
    }



}



Lo faccio in questo modo:

try {
    YourClass yourClass = new YourClass();
    Method method = YourClass.class.getMethod("yourMethodName", ParameterOfThisMethod.class);
    method.invoke(yourClass, parameter);
} catch (Exception e) {
    e.printStackTrace();
}



Object obj;

Method method = obj.getClass().getMethod("methodName", null);

method.invoke(obj, null);



Method method = someVariable.class.getMethod(SomeClass);
String status = (String) method.invoke(method);

SomeClass è la classe e someVariable è una variabile.




per me un modo abbastanza semplice e folle sarebbe semplicemente quello di fare un metodo metodo di chiamata in questo modo:

public static object methodCaller(String methodName)
{
    if(methodName.equals("getName"))
        return className.getName();
}

quindi quando hai bisogno di chiamare il metodo, metti semplicemente qualcosa di simile

//calling a toString method is unnessary here, but i use it to have my programs to both rigid and self-explanitory 
System.out.println(methodCaller(methodName).toString()); 



Questo suona come qualcosa che è fattibile con il pacchetto Java Reflection.

http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html

In particolare in Metodi di richiamo per nome:

importare java.lang.reflect. *;

public class method2 {
  public int add(int a, int b)
  {
     return a + b;
  }

  public static void main(String args[])
  {
     try {
       Class cls = Class.forName("method2");
       Class partypes[] = new Class[2];
        partypes[0] = Integer.TYPE;
        partypes[1] = Integer.TYPE;
        Method meth = cls.getMethod(
          "add", partypes);
        method2 methobj = new method2();
        Object arglist[] = new Object[2];
        arglist[0] = new Integer(37);
        arglist[1] = new Integer(47);
        Object retobj 
          = meth.invoke(methobj, arglist);
        Integer retval = (Integer)retobj;
        System.out.println(retval.intValue());
     }
     catch (Throwable e) {
        System.err.println(e);
     }
  }
}



Links