2009-05-19 6 views
107

J'ai une classe qui utilise XML et la réflexion pour retourner Object s à une autre classe.N'importe quel moyen d'appeler une méthode privée?

Normalement, ces objets sont des sous-champs d'un objet externe, mais de temps en temps, c'est quelque chose que je veux générer à la volée. J'ai essayé quelque chose comme ça mais en vain. Je crois que c'est parce que Java ne vous permettra pas d'accéder aux méthodes private pour la réflexion.

Element node = outerNode.item(0); 
String methodName = node.getAttribute("method"); 
String objectName = node.getAttribute("object"); 

if ("SomeObject".equals(objectName)) 
    object = someObject; 
else 
    object = this; 

method = object.getClass().getMethod(methodName, (Class[]) null); 

Si la méthode fournie est private, il échoue avec un NoSuchMethodException. Je pourrais le résoudre en faisant la méthode public, ou en faisant une autre classe pour en tirer. Bref, je me demandais simplement s'il était possible d'accéder à la méthode private par réflexion.

Répondre

233

Vous pouvez appeler une méthode privée avec réflexion. Modification du dernier bit du code affiché:

Method method = object.getClass().getDeclaredMethod(methodName); 
method.setAccessible(true); 
Object r = method.invoke(object); 

Il existe plusieurs avertissements. Tout d'abord, getDeclaredMethod ne trouvera que la méthode déclarée dans le Class actuel, non héritée de supertypes. Parcourez donc la hiérarchie de classes concrètes si nécessaire. Deuxièmement, un SecurityManager peut empêcher l'utilisation de la méthode setAccessible. Donc, il peut avoir besoin de fonctionner comme PrivilegedAction (en utilisant AccessController ou Subject).

+1

quand je l'ai fait dans le passé, j'ai aussi appelé method.setAccessible (false) après avoir appelé la méthode, mais je n'ai aucune idée si cela est nécessaire ou non. – shsteimer

+12

Non, lorsque vous définissez l'accessibilité, elle s'applique uniquement à cette instance. Tant que vous ne laissez pas cet objet Method échapper à votre contrôle, c'est sûr. – erickson

+5

Je retourne absolument l'amour vous mate. Réponse géniale + mauvais code == pas si mauvais jour. – droope

32

Utilisez getDeclaredMethod() pour obtenir un objet Method privé, puis utilisez method.setAccessible() pour autoriser l'appel réel.

+7

La réponse acceptée est réellement complète. –

+0

Dans mon propre exemple (http://stackoverflow.com/a/15612040/257233), j'obtiens un 'java.lang.StackOverflowError' si je n'appelle pas' setAccessible (true) '. –

21

Si la méthode accepte le type de données non-primitive alors la méthode suivante peut être utilisée pour invoquer une méthode privée de toute catégorie:

public static Object genericInvokMethod(Object obj, String methodName, 
      int paramCount, Object... params) { 
     Method method; 
     Object requiredObj = null; 
     Object[] parameters = new Object[paramCount]; 
     Class<?>[] classArray = new Class<?>[paramCount]; 
     for (int i = 0; i < paramCount; i++) { 
      parameters[i] = params[i]; 
      classArray[i] = params[i].getClass(); 
     } 
     try { 
      method = obj.getClass().getDeclaredMethod(methodName, classArray); 
      method.setAccessible(true); 
      requiredObj = method.invoke(obj, params); 
     } catch (NoSuchMethodException e) { 
      e.printStackTrace(); 
     } catch (IllegalArgumentException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } catch (InvocationTargetException e) { 
      e.printStackTrace(); 
     } 

     return requiredObj; 
    } 

Le paramètre accepté sont obj, methodName, le nombre de paramètres acceptés et Les paramètres. Par exemple

public class Test { 
private String concatString(String a, String b) { 
    return (a+b); 
} 
} 

Méthode concatString peut être invoquée comme

Test t = new Test(); 
    String str = (String) genericInvokMethod(t, "concatString", 2, "Hello", "Mr.x"); 
+5

Pourquoi _paramCount_ est-il nécessaire? Ne pouvez-vous pas simplement utiliser * params.length *? –

Questions connexes