wake-up-neo.com

Ruft den Namen eines Feldes ab

Ist es in Java möglich, einen Feldnamen in Zeichenfolge aus dem tatsächlichen Feld abzurufen? mögen:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

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

PS: Ich bin nicht auf der Suche nach der Klasse/dem Klassennamen eines Feldes oder erhalte Field from name in String.


BEARBEITEN: Wenn ich es mir anschaue, würde ich wahrscheinlich keine Methode benötigen, um den Namen eines Feldes zu erhalten, die Field-Instanz (aus dem "Codenamen" des Feldes) würde ausreichen. [z.B. Field myField = getField(linkTool)]

Es gibt wahrscheinlich nichts, was ich in Java selbst haben möchte. Ich werde einen Blick auf die ASM-Bibliothek werfen, aber am Ende könnte ich den String als Bezeichner für Felder verwenden: /


EDIT2: Mein Englisch ist nicht so gut (aber selbst in meiner Muttersprache hätte ich Probleme, das zu erklären), also füge ich noch ein Beispiel hinzu. Hoffentlich wird es jetzt klarer:

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));
        }
    }
}

Die Frage ist: Wie könnte die constructItemIdeal-Methode aussehen? (Nach Antworten und Nachforschen ist das in Java nicht möglich, aber wer weiß ...)

23
monnef

Leider gibt es keine Möglichkeit, das zu tun, was Sie wünschen. Warum?

In einer Zeit, in der wir immer noch versuchen zu beweisen, dass Java nicht langsam ist, hätte das Speichern von Metadaten, wo oder wie ein Objekt erstellt wurde, einen großen Overhead, und da es einen sehr geringen Verwendungsbereich hat, existiert es nicht. Nicht nur das: Es gibt auch eine Reihe technischer Gründe.

Ein Grund liegt in der Implementierung der JVM. Die Spezifikation für das Java-Format class finden Sie hier . Es ist ein Bissen, aber es ist sehr informativ. 

Wie ich bereits gesagt habe, kann ein Objekt von überall her konstruiert werden, sogar in Situationen, in denen Objekte keine Namen haben. Nur als Klassenmitglieder definierte Objekte haben Namen (aus offensichtlichen Gründen des Zugriffs): Objekte, die in Methoden erstellt wurden, haben keine Namen. Die JVM verfügt über eine lokale Variablentabelle mit maximal 65535 Einträgen. Diese werden in den Stack geladen und über die Opcodes 'load' und 'store' in der Tabelle gespeichert.

Mit anderen Worten, eine Klasse gefällt

public class Test {
int class_member = 42;

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

wird in kompiliert

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--    
}

Dort sehen Sie ein klares Beispiel aus javap -v Test, dass class_member in der lokalen Variablentabelle mit Index 1 abstrahiert wurde, während der Name my_local_field beibehalten wird (0 ist für this reserviert).

Dies wäre an sich schon ein Problem für Debugger und so, daher wurde eine Reihe von Attributen (Denken Sie Metadaten für Klassendateien) entworfen. Dazu gehören die LocalVariableTable , LineNumberTable und LocalVariableTypeTable . Diese Attribute sind hilfreich für Stacktraces (LineNumberTable) und Debugger (LocalVariableTable und LocalVariableTypeTable). Dies sind jedoch vollständig optional, um sie in Dateien aufzunehmen, und es gibt keine Garantie dafür, dass sie Folgendes sind:

Das LineNumberTable-Attribut ist ein optionales Attribut mit variabler Länge in der Attributtabelle

Dasselbe gilt für den Rest von ihnen.

Darüber hinaus produzieren die meisten Compiler standardmäßig nur die LineNumberTable, sodass Sie selbst dann, wenn es möglich wäre, kein Glück hat.

Grundsätzlich wäre es für Entwickler ziemlich frustrierend, einen getFieldName(object) zu haben (was für lokale Variablen überhaupt nicht möglich wäre) und nur dann funktionieren sollte, wenn diese Attribute vorhanden sind. 

Sie sind also auf absehbare Zeit mit getField mit Strings beschäftigt. Shameless plug: IntelliJ scheint mit Refactoren gut überlegt zu sein: Nachdem ich Minecraft Mods geschrieben habe, kenne ich das Gefühl und kann sagen, dass die Arbeit beim Refactoring von IntelliJ erheblich reduziert wird. Dasselbe gilt wahrscheinlich auch für die meisten modernen IDEs: Ich bin mir sicher, dass die großen Player Eclipse, Netbeans et al. verfügen über gut implementierte Refactoring-Systeme.

13
Xyene

Wenn das Feld privat ist, können Sie es zugänglich machen:

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

Gehen Sie folgendermaßen vor, um den Namen des Felds zu erhalten:

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

Wenn Sie den Namen des Feldes nicht kennen ... Nun, welches Feld möchten Sie erhalten? :) Sie könnten ALLE Felder mit Reflektion erhalten und dann durchlaufen. Wenn Sie das Feld jedoch nicht kennen, was ist der eigentliche Sinn und die eigentliche Logik in dieser Aktion?

7
user

Dies ist nur durch Reflektion möglich.

Verwenden Sie Class.getDeclaringFields () Und 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

Ich denke, das ist, wonach du suchst.

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

Meine Motivation beim Abrufen des Feldnamens besteht darin, nach Dingen in einer Schlüsselwertdatenbank (z. B. Mongodb) zu suchen. Ich habe eine Struktur mit Mitgliedern (Feldern), die die gespeicherten Daten darstellt. Ich benutze die Mitgliedsnamen, um die Datenbank zu durchsuchen .

Mein Vorschlag ist, jedem wichtigen Mitglied einen statischen Getter zu geben. Beispiel:

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

Hoffentlich hat Java dazu in der Zukunft einen Mechanismus, da die Metadaten heutzutage Teil der Daten sein können. Vor allem, wenn es um JSON geht.

1

Dies kann mit Hilfe der Laufzeit-Bytecode-Instrumentierung erfolgen, z. B. mit der Byte Buddy-Bibliothek. Siehe Antwort nameof-Entsprechung in Java

0
jreznot