获取字段的名称



在Java中,是否可以从实际字段中获取字符串中字段的名称?类似:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;
    public void xxx{
        String fieldsName = *getFieldsName(linkTool)*;
    }
}

PS:我不是在寻找字段的类名或从字符串中的名称中获取字段。


编辑:当我查看它时,我可能不需要一个方法来获取字段的名称,field实例(来自字段的"代码名称")就足够了。[例如Field myField = getField(linkTool)]

Java本身可能没有我想要的东西。我将查看ASM库,但最终我可能会使用字符串作为字段的标识符:/


第2版:我的英语不太好(但即使用我的母语,我也很难解释这一点),所以我再加一个例子。希望现在会更清楚:

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_member被保留,但my_local_field已被抽象为局部变量表中的索引1(0为this保留)。

这本身会让调试器等感到痛苦,因此设计了一组属性(比如类文件的元数据)。其中包括LocalVariableTable、LineNumberTable和LocalVariableTypeTable。这些属性对于堆栈跟踪(LineNumberTable)和调试器(LocalVariableTable和LocalVariableTypeTable)非常有用。然而,这些完全可以选择包含在文件中,并且不能保证它们是:

LineNumberTable属性是可选的可变长度属性在属性表中

其他人也是如此。

此外,默认情况下,大多数编译器只生成LineNumberTable,所以即使可能,您也会运气不佳。

基本上,对于开发人员来说,拥有getFieldName(object)(对于局部变量来说,这在一开始是不可能的),并且只有当这些属性存在时才让它工作,这将是非常令人沮丧的。

因此,在可预见的未来,将getField与Strings结合使用会让您陷入困境。无耻的插件:IntelliJ似乎很好地处理了带有反射的重构:我也写了Minecraft mod,我知道这种感觉,可以说IntelliJ大大减少了重构工作。但对于大多数现代IDE来说,情况可能也是如此:我相信大型企业,Eclipse、Netbeans都有实现良好的重构系统。

如果字段是私有的,您可以访问它:

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.

如果你不知道字段的名称。。。好那么你想获得哪个领域?:)你可以使用反射获得所有的字段,然后循环通过它们。。但如果你不了解这个领域,那么这个行动的实际意义和逻辑是什么?

如何使用lombok注释@FieldNameConstants

@FieldNameConstants
public class Mod {
    public ItemLinkTool linkTool;
}
...
String fieldName = Mod.Fields.linkTool; //fieldName == "linkTool"

有了这个功能,我们就不需要维护用于获取字段名称的代码(lombok为我们做这件事)。删除字段后,编译器会抱怨它的使用情况。

请注意,此功能标记为实验性功能。

字符串字段名称=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;
}

这只能通过反射实现。

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

我获取字段名的动机是使用它来搜索键值数据库(如mongodb)中的内容。我有一个结构,其成员(字段)表示保存的数据我使用成员名称来搜索数据库

我的建议是为每个重要成员设置一个静态getter。示例:

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

希望Java将来能有一些机制来实现这一点,因为现在,元数据可能是数据的一部分。尤其是在使用JSON时。

它可以使用运行时字节码插入来完成,例如使用byte Buddy库,请参阅Java中的等效答案名称

以及Lombok的@FieldNameConstants。可以使用pojo-analyzers,它还可以访问字段的getter和setter(无需反射)。

@DetailedPojo
public class Mod {
    public ItemLinkTool linkTool;
}
...
Mod mod = new Mod(myItemLinkTool);
DetailedMod.map.get("entityId").getFieldValue(mod); // myItemLinkTool

相关内容

  • 没有找到相关文章