web-dev-qa-db-fra.com

Obtenir le nom d'un champ

Est-il possible en Java d'obtenir un nom de champ dans string à partir du champ actuel? comme:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

    public void xxx{
        String fieldsName = *getFieldsName(linkTool)*;
    }
}

PS: Je cherche non à rechercher le nom de la classe/classe d’un champ ou à obtenir le champ à partir du nom dans String.


EDIT: Quand je le regarde, je n’aurais probablement pas besoin d’une méthode pour obtenir le nom d’un champ, l’instance Field (à partir du "nom de code" du champ) suffirait. [par exemple. Field myField = getField(linkTool)

Il n'y a probablement rien de ce que je veux dans Java même. Je vais jeter un coup d'œil à la bibliothèque ASM, mais je pourrais finir par utiliser la chaîne comme identifiant pour les champs: /


EDIT2: Mon anglais n’est pas très bon (mais même dans ma langue maternelle, j’aurais du mal à expliquer cela), j’ajoute un autre exemple. Espérons que ce sera plus clair maintenant:

public class mod2 {
    @ItemID
    public static ItemLinkTool linkTool;

    @ItemID
    public static ItemLinkTool linkTool2;

    @ItemID
    public static ItemPipeWrench pipeWrench;

    public void constructItems() {
        // most trivial way
        linkTool = new ItemLinkTool(getId("linkTool"));
        linkTool2 = new ItemLinkTool(getId("linkTool2"));
        pipeWrench = new ItemPipeWrench(getId("pipeWrench"));

        // or when constructItem would directly write into field just
        constructItem("linkTool");
        constructItem("linkTool2");
        constructItem("pipeWrench");

        // but I'd like to be able to have it like this
        constructItemIdeal(linkTool);
        constructItemIdeal(linkTool2);
        constructItemIdeal(pipeWrench);
    }

    // not tested, just example of how I see it
    private void constructItem(String name){
        Field f = getClass().getField(name);
        int id = getId(name);

        // this could be rewritten if constructors take same parameters
        // to create a new instance using reflection
        if (f.getDeclaringClass() == ItemLinkTool){
            f.set(null, new ItemLinkTool(id));
        }else{
            f.set(null, new ItemPipeWrench(id));
        }
    }
}

La question est la suivante: à quoi pourrait ressembler la méthode constructItemIdeal? (D'après les réponses et la recherche sur Google, ce n'est pas possible en Java, mais qui sait ..)

23
monnef

Malheureusement, il n'y a aucun moyen de faire ce que vous souhaitez. Pourquoi?

À une époque où nous essayons encore de prouver que Java n'est pas lent, stocker des métadonnées sur l'endroit ou la manière dont un objet a été construit aurait un coût important, et comme il a une plage d'utilisation très minime, il n'existe pas. Et pas seulement ça: il y a aussi beaucoup de raisons techniques.

Une des raisons est liée à la manière dont la machine virtuelle est implémentée. La spécification du format de fichier Java class peut être trouvée ici . C'est une bouchée, mais c'est très instructif. 

Comme je l'ai dit précédemment, un objet peut être construit de n'importe où, même dans les situations où objets n'ont pas de nom. Seuls les objets définis en tant que membres de la classe ont des noms (pour la raison évidente d'accès), contrairement aux objets construits dans des méthodes. La machine virtuelle Java dispose d'une table de variables locale avec un maximum de 65 535 entrées. Ceux-ci sont chargés dans la pile et stockés dans la table via les opcodes * load et * store.

En d'autres termes, une classe comme

public class Test {
int class_member = 42;

   public Test() {
      int my_local_field = 42;
   }
}

est compilé dans

public class Test extends Java.lang.Object
  SourceFile: "Test.Java"
  minor version: 0
  major version: 50
  Constant pool:
  --snip--

{
int class_member;

public Test();
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   aload_0
   5:   bipush  42
   7:   putfield        #2; //Field class_member:I
   10:  bipush  42
   12:  istore_1
   13:  return
  LineNumberTable:
   --snip--    
}

Ici, vous pouvez voir un exemple clair tiré de javap -v Test qui, bien que le nom class_member soit conservé, le my_local_field a été extrait de l’index 1 dans la table de variables locales (0 est réservé à this).

Cela en soi serait difficile pour les débogueurs et autres. Un ensemble d’attributs (pensez aux métadonnées pour les fichiers de classe) a donc été conçu. Il s'agit notamment de LocalVariableTable , LineNumberTable et LocalVariableTypeTable . Ces attributs sont utiles pour stacktraces (LineNumberTable) et les débogueurs (LocalVariableTable et LocalVariableTypeTable). Cependant, ils sont complètement facultatifs à inclure dans les fichiers, et rien ne garantit qu'ils le sont:

L'attribut LineNumberTable est un attribut facultatif de longueur variable dans la table d'attributs

La même chose vaut pour le reste d'entre eux.

En outre, la plupart des compilateurs ne produisent rien par défaut, mais LineNumberTable. Vous risquez donc d’être malchanceux, même si cela était possible.

En gros, il serait très frustrant pour les développeurs d’avoir une getFieldName(object) (ce qui ne serait pas possible en premier lieu pour les variables locales) et de ne le faire fonctionner que lorsque ces attributs sont présents. 

Vous êtes donc bloqué dans un avenir prévisible avec getField with Strings. Shameless plug: IntelliJ semble bien gérer les refacteurs: après avoir écrit les mods de Minecraft, je connais le sentiment, et je peux dire que le travail de refactorisation est considérablement réduit par IntelliJ. Mais la même chose est probablement vraie pour la plupart des IDE modernes: je suis sûr que les gros joueurs, Eclipse, Netbeans et al. ont mis en place des systèmes de refactoring bien implantés.

13
Xyene

Si le champ est privé, vous pouvez le rendre accessible:

Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
String value = (String)field.get(object); // to get the value, if needed. In the example is string type

Pour avoir le nom du champ, allez comme ça:

//use the field object from the example above
field.getName(); // it is in String.

Si vous ne connaissez pas le nom du champ ... eh bien ... alors quel domaine aimeriez-vous avoir? :) Vous pouvez obtenir TOUS les champs en utilisant la réflexion puis les parcourir en boucle. Mais si vous ne connaissez pas le champ, quels sont le sens et la logique de cette action?

7
user

Ceci n'est possible que par réflexion.

Utilisez Class.getDeclaringFields () Et Java.reflection.Field.getName()

6
AlexWien

String fieldName = getFieldName (linkTool, this);

private static String getFieldName(Object fieldObject, Object parent) {

    Java.lang.reflect.Field[] allFields = parent.getClass().getFields();
    for (Java.lang.reflect.Field field : allFields) {
        Object currentFieldObject;
        try {
            currentFieldObject = field.get(parent);
        } catch (Exception e) {
            return null;
        }
        boolean isWantedField = fieldObject.equals(currentFieldObject);
        if (isWantedField) {
            String fieldName = field.getName();
            return fieldName;
        }
    }
    return null;
}
4
Stefan

Je pense que c'est ce que vous recherchez.

SomeClass data; // or List<SomeClass> data; etc.
List<String> fieldNames = getFieldNamesForClass(data.get(0).getClass());

private static List<String> getFieldNamesForClass(Class<?> clazz) throws Exception {
    List<String> fieldNames = new ArrayList<String>();
    Field[] fields = clazz.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        fieldNames.add(fields[i].getName());
    }
    return fieldNames;
}
4
Oguz

Ma motivation pour obtenir le nom du champ est de l'utiliser pour rechercher des éléments dans une base de données de valeurs-clés (comme mongodb). J'ai une structure avec des membres (champs) qui représente les données sauvegardées. J'utilise les noms de membres pour rechercher la base de données .

Ma suggestion est de mettre un getter statique pour chaque membre important. Exemple:

public class Data {
  public static String getMember1Key() {
    return "member1";
  }
  public String member1;
}

J'espère que Java aura un mécanisme pour cela à l'avenir, car de nos jours, les métadonnées peuvent faire partie des données. Surtout quand on fait des choses avec JSON.

1
AlikElzin-kilaka

Cela peut être fait en utilisant une instrumentation de code octet à l'exécution, par exemple en utilisant la bibliothèque Byte Buddy, voir cette réponse nomd'équivalent en Java

0
jreznot