簡體   English   中英

獲取字段名稱

[英]Get name of a field

在Java中是否可以從實際字段中獲取字符串中的字段名稱? 喜歡:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

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

PS:我不是在尋找字段的類/類名稱或從字符串中的名稱獲取字段。


編輯:當我查看它時,我可能不需要獲取字段名稱的方法,Field 實例(來自字段的“代號”)就足夠了。 [例如Field myField = getField(linkTool) ]

Java 本身可能沒有我想要的東西。 我將看一下 ASM 庫,但最后我可能最終會使用字符串作為字段的標識符:/


EDIT2:我的英語不是很好(但即使用我的母語我也很難解釋這一點),所以我再添加一個例子。 希望現在會更清楚:

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

問題是:constructItemIdeal 方法怎么看? (根據答案和谷歌搜索,我認為這在 Java 中是不可能的,但誰知道......)

不幸的是,沒有辦法做你想做的事。 為什么?

在我們仍然試圖證明Java不慢的時代,存儲構造對象的位置或方式的元數據會產生很大的開銷,並且因為它具有非常小的使用范圍,所以它不存在。 不僅如此:還有許多技術原因。

一個原因是因為JVM的實現方式。 可以在此處找到Java class文件格式的規范。 這是非常滿口的,但它非常有用。

正如我之前所說,可以從任何地方構建對象,甚至是對象沒有名稱的情況 只有定義為類成員的對象才具有名稱(出於顯而易見的原因):在方法中構造的對象不具有名稱。 JVM有一個局部變量表,最多包含65535個條目。 它們被加載到堆棧並通過* load和* store操作碼存儲到表中。

換句話說,一個類似的

public class Test {
int class_member = 42;

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

被編譯成

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

在那里,您可以從javap -v Test中看到一個明確的示例,即保留名稱class_membermy_local_field已被抽象為局部變量表中的索引1( this保留0)。

這本身對於調試器來說是一種痛苦,因此設計了一組屬性(想想類文件的元數據)。 這些包括LocalVariableTableLineNumberTableLocalVariableTypeTable 這些屬性對堆棧跟蹤(LineNumberTable)和調試器(LocalVariableTable和LocalVariableTypeTable)非常有用。 但是,這些是完全可選的包含在文件中,並且不能保證它們是:

LineNumberTable屬性是屬性表中的可選變長屬性

其他人也是如此。

此外,大多數編譯器默認情況下除了LineNumberTable之外不會產生任何東西,所以即使可能,你也會運氣不好。

基本上,對於開發人員來說,擁有一個getFieldName(object) (這在本地變量的首位是不可能的getFieldName(object)是非常令人沮喪的,並且只有當這些屬性存在時才能使用它。

因此,在可預見的將來,使用帶字符串的getField遇到getField 無恥的插件: IntelliJ似乎很好地處理具有反射的重構:編寫Minecraft mods以及我知道這種感覺,並且可以說IntelliJ顯着減少了重構工作。 但對於大多數現代IDE來說也是如此:我確信大型玩家Eclipse,Netbeans 等。 擁有良好實施的重構系統。

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

如果該字段是私有的,您可以訪問它:

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

要獲得字段的名稱,請執行以下操作:

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

如果您不知道該字段的名稱......那么..那么您希望得到什么字段? :)您可以使用反射獲取所有字段然后循環它們。但是如果您不知道該字段,此操作中的實際意義和邏輯是什么?

這只能通過反射來實現。

使用Class.getDeclaringFields ()和java.reflection.Field.getName()

我想這就是你要找的東西。

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

如何使用lombok注釋@FieldNameConstants

然后您可以通過以下方式訪問您的字段名稱: mod.Field.linkTool

這樣我們就不需要維護獲取字段名稱的代碼(lombok 為我們做的),當我們刪除該字段時,編譯器會抱怨它的使用。

請注意,此功能被標記為實驗性的。

參考鏈接

獲取字段名稱的動機是使用它來搜索鍵值數據庫中的內容(如mongodb)。 我有一個結構,其成員(字段)代表保存的數據。 我使用成員名稱來搜索數據庫

我的建議是為每個重要成員放置一個靜態getter。 例:

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

希望Java將來會有一些機制,因為現在,元數據可能是數據的一部分。 特別是在使用JSON時。

它可以使用運行時字節代碼檢測來完成,例如使用Byte Buddy庫, 在Java中查看此等效答案名稱

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM