2009-05-28 9 views
2

J'ai joué avec la réflexion en Java ... et je suis un peu déconcerté. J'espérais que le programme ci-dessous me permettrait de changer la valeur d'une variable membre public au sein d'une classe. Cependant, je reçois une exception IllegalArgumentException. Des idées?IllegalArgumentException lors de la définition du membre public

public class ColinTest { 

    public String msg = "fail"; 

    public ColinTest() { } 

    public static void main(String args[]) throws Exception { 
     ColinTest test = new ColinTest(); 
     Class c = test.getClass(); 
     Field[] decfields = c.getDeclaredFields(); 
     decfields[0].set("msg", "success"); 

     System.out.println(ColinTest.msg) 
    } 
} 

Je reçois ce message -

Exception in thread "main" java.lang.IllegalArgumentException 
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37) 
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:57) 
    at java.lang.reflect.Field.set(Field.java:656) 
    at ColinTest.main(ColinTest.java:44) 

Merci.

Répondre

7

Le premier argument de la méthode Field.set doit être l'objet sur lequel vous réfléchissez.

decfields[0].set("msg", "success"); 

lire:

decfields[0].set(test, "success"); 

En outre, l'appel System.out.println final devrait se référer à l'objet test plutôt que la classe ColinTest, comme je suppose que l'intention est de sortir le contenu du champ test.msg.

Mise à jour

Comme l'a souligné toolkit et Chris, la méthode Class.getDeclaredField peut être utilisé pour spécifier le nom du champ afin de le récupérer:

Field msgField = test.getClass().getDeclaredField("msg"); 

// or alternatively: 

Field msgField = ColinTest.class.getDeclaredField("msg"); 

Ensuite, la méthode set du msgField peut être appelé comme:

msgField.set(test, "success"); 

Cette façon de faire a l'avantage, comme l'a déjà souligné Toolkit, de rajouter plusieurs champs à l'objet, l'ordre des champs renvoyés par Class.getDeclaredFields ne renvoie pas nécessairement le champ msg comme premier élément du tableau. En fonction de l'ordre du tableau renvoyé pour être d'une certaine manière peut causer des problèmes lorsque des modifications sont apportées à la classe.

Par conséquent, il serait probablement une meilleure idée d'utiliser getDeclaredField et de déclarer le nom du champ désiré.

+0

Faites attention en utilisant 'decFields [0]' si vous prévoyez d'ajouter plus de champs dans le futur! – toolkit

2

Le premier arg à définir() devrait être l'objet dont vous modifiez le champ ... à savoir test.

1

Lorsque vous appelez getDeclaredFields, chaque élément de tableau contiendra un objet Field qui représente un champ sur la classe, non sur un objet instancié.

C'est pourquoi vous devez spécifier l'objet sur lequel vous souhaitez définir ce domaine, lors de l'utilisation du poseur:

ColinTest test = new ColinTest(); 
Field msgfield = ColinTest.class.getDeclaredField("msg"); 
msgField.set(test, "success"); 
+0

Votre premier arg devrait être test, pas c. – toolkit

+0

Merci, j'ai vu ça aussi, juste après avoir posté, donc je l'ai corrigé dans un montage tout de suite ;-) –

1

Qu'est-ce que vous voulez est:

Field msgField = c.getDeclaredField("msg"); 
msgField.set(test, "Success"); 

Prenez soin à l'aide decfields[0] depuis vous pouvez ne pas obtenir ce que vous attendiez lorsque vous ajoutez un deuxième champ à votre classe (vous ne vérifiez pas que decfields[0] correspond au champ msg)

2

S'il vous plaît assurez-vous que le code que vous postez compile effectivement (vous voulez test.msg, pas ColinTest.msg).

Vous pouvez également envisager d'utiliser une version plus récente de Java, qui pourrait fournir un message d'erreur plus spécifique:

% java ColinTest 
Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.String field ColinTest.msg to java.lang.String 
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146) 
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150) 
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37) 
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:57) 
    at java.lang.reflect.Field.set(Field.java:657) 
    at ColinTest.main(ColinTest.java:13) 

qui probablement vous ont conduit aux autres de solution ont posté.

0

J'ai trébuché accros cette page, parce que bizarrement, je ne peux pas définir le champ chaîne publique dans ma classe. Le code ajoutera une nouvelle ligne dans ArrayList dans chaque boucle for. Le problème est, je mets le code d'instanciation du nouvel objet (en utilisant la réflexion qui est) une seule fois, en dehors de l'intérieur pour. Vérifiez que maintenant je déplace le Class.forName (fqnModel) en dehors de la boucle while. Parce que nous n'avons besoin de créer l'objet Class qu'une seule fois. Mais ensuite, avant chaque boucle for, je crée un objet modèle qui sera éventuellement ajouté dans une ArrayList.

Pour être clair, ceci est mon BiroModel look classe comme:

public class BiroModel extends Model { 
public String idbiro = ""; 
public String biro = ""; 

public BiroModel() { 
} 

public BiroModel(String table, String pkField) { 
    super(table, pkField); 
    fqn = BiroModel.class.getName(); 

} 

public String getBiro() { 
    return biro; 
} 

public void setBiro(String biro) { 
    this.biro = biro; 
} 

public String getIdbiro() { 
    return idbiro; 
} 

public void setIdbiro(String idbiro) { 
    this.idbiro = idbiro; 
} 

}

je crée une convention ici, que tous les objets sur le terrain doit être déclarée publique. Mais, parce que j'ai besoin de la syntaxe EL, j'ai encore besoin de créer getter/setter pour ce domaine public.

Questions connexes