Java 获取字段的名称

Java 获取字段的名称,java,reflection,field,Java,Reflection,Field,在Java中,是否可以从实际字段中获取字符串形式的字段名?比如: public class mod { @ItemID public static ItemLinkTool linkTool; public void xxx{ String fieldsName = *getFieldsName(linkTool)*; } } 注:我不是在寻找字段的类/类名,也不是在从字符串中的名称获取字段 编辑: 当我查看它时,我可能不需要方法来获取字段的

在Java中,是否可以从实际字段中获取字符串形式的字段名?比如:

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

    public void xxx{
        String fieldsName = *getFieldsName(linkTool)*;
    }
}
注:我不是在寻找字段的类/类名,也不是在从字符串中的名称获取字段


编辑: 当我查看它时,我可能不需要方法来获取字段的名称,字段实例(来自字段的“代码名”)就足够了。[例如,
字段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));
        }
    }
}

问题是:理想的方法看起来如何?(从答案和谷歌搜索来看,这在Java中是不可能的,但谁知道呢?)

这只能通过反射实现

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

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

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.

如果您不知道字段的名称。。。好。。那么您希望获得哪个字段?:)您可以使用反射获取所有字段,然后循环遍历它们。。但是如果你不知道这个字段,那么这个动作的实际意义和逻辑是什么?

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

在一个我们仍在努力证明Java并不慢的时代,存储对象在何处或如何构造的元数据将有很大的开销,而且因为它的使用范围非常小,所以它并不存在。不仅如此,还有很多技术原因

一个原因是JVM是如何实现的。可以找到Java
文件格式的规范。这是一个相当多的口,但它是非常有益的

如前所述,对象可以从任何地方构造,即使在对象没有名称的情况下也是如此。只有定义为类成员的对象才有名称(出于访问的明显原因):在方法中构造的对象没有名称。JVM有一个本地变量表,最多有65535个条目。它们被加载到堆栈中,并通过*加载和*存储操作码存储到表中

换句话说,像

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--    
}
公共类测试扩展了java.lang.Object
源文件:“Test.java”
次要版本:0
主要版本:50
固定池:
--剪断--
{
国际级会员;
公开考试();
代码:
堆栈=2,局部变量=2,参数大小=1
0:aload_0
1:invokespecial#1;//方法java/lang/Object。“:()V
4:aload_0
5:bipush 42
7:putfield#2;//字段类_成员:I
10:bipush 42
12:istore_1
13:返回
LineNumberTable:
--剪断--
}
在这里,您可以从
javap-v Test
中看到一个清晰的示例,即当名称
class\u成员
被保留时,
my\u local\u字段
已被抽象到局部变量表中的索引1中(0为
保留)

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

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

其他人也是如此

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

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


因此,在可预见的未来,您将无法使用带字符串的
getField
。无耻的插件:似乎能很好地处理反射重构:我也写过Minecraft mods,我知道这种感觉,可以说IntelliJ大大减少了重构工作。但对于大多数现代IDE来说,情况可能也是如此:我相信大型IDE公司Eclipse、Netbeans等都已经很好地实现了重构系统。

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

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

public class Data {
  public static String getMember1Key() {
    return "member1";
  }
  public String member1;
}
希望Java将来能为此提供某种机制,因为现在元数据可能是数据的一部分。尤其是在使用JSON时

字符串fieldName=getFieldName(linkTool,this)


可以使用运行时字节码指令插入来完成,例如使用字节伙伴库,请参见此答案,我想这就是您要寻找的

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;
}
SomeClass数据;//或列出数据;等
List fieldNames=getFieldNamesForClass(data.get(0.getClass());
私有静态列表getFieldNamesForClass(类clazz)引发异常{
List fieldNames=new ArrayList();
Field[]fields=clazz.getDeclaredFields();
for(int i=0;i
如果字段不是私有的,则可以从代码的特定部分知道引用的字段的名称,但要分析字节码。您可以使用ASM库进行此操作。字段名称是什么意思?您的示例中的字符串是“linkTool”吗?您是指cla字段的名称吗
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;
}