获取字段的名称

2022-09-01 03:40:58

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

问题是:如何看构造ItemIdeal方法?(从答案和谷歌搜索中,我发现这在Java中是不可能的,但谁知道呢..)


答案 1

不幸的是,没有办法做你想做的事。为什么?

在一个我们仍然试图证明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--    
}

在那里,您可以看到一个明显的例子,在保留名称的同时,已抽象为局部变量表中的索引 1(0 保留给 )。javap -v Testclass_membermy_local_fieldthis

这本身对调试器等来说将是一个痛苦,因此设计了一组属性(想想类文件的元数据)。其中包括 LocalVariableTableLineNumberTableLocalVariableTypeTable。这些属性对于堆栈跟踪(LineNumberTable)和调试器(LocalVariableTable和LocalVariableTypeTable)非常有用。但是,这些是完全可选的,可以包含在文件中,并且不能保证它们是:

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

其余的人也是如此。

此外,默认情况下,大多数编译器除了 LineNumberTable 之外不生成任何内容,因此即使有可能,您也不会运气好。

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

因此,在可预见的未来,您将被困在与Strings一起使用时。无耻插件:IntelliJ似乎很好地处理了反射的重构:在编写了Minecraft模组之后,我也知道这种感觉,可以说IntelliJ大大减少了重构工作。但对于大多数现代IDE来说,情况可能也是如此:我敢肯定,Eclipse、Netbeans大玩家已经很好地实现了重构系统。getField


答案 2

如果该字段是私有的,您可以使其可访问:

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.

如果您不知道该字段的名称...井。。那么你想得到什么领域?:)您可以使用反射获取所有字段,然后循环访问它们。但是,如果你不了解这个领域,那么这个动作的实际意义和逻辑是什么?