获取字段的名称

mon*_*nef 28 java reflection field

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

public class mod {
    @ItemID
    public static ItemLinkTool linkTool;

    public void xxx{
        String fieldsName = *getFieldsName(linkTool)*;
    }
}
Run Code Online (Sandbox Code Playgroud)

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是:如何看看constructItemIdeal方法?(从答案和谷歌搜索我的事情,这是不可能在Java,但谁知道..)

mak*_*aki 19

使用lombok注解怎么样@FieldNameConstants

@FieldNameConstants
public class Mod {
    public ItemLinkTool linkTool;
}
...
String fieldName = Mod.Fields.linkTool; //fieldName == "linkTool"
Run Code Online (Sandbox Code Playgroud)

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

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

  • 谢谢,我不知道 Lombok 也提供这个功能! (3认同)

Xye*_*ene 16

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

在我们仍然试图证明Java不慢的时代,存储构造对象的位置或方式的元数据会产生很大的开销,并且因为它具有非常小的使用范围,所以它不存在.不仅如此:还有许多技术原因.

一个原因是因为JVM的实现方式.class可以在此处找到Java 文件格式的规范.这是非常满口的,但它非常有用.

正如我之前所说,可以从任何地方构建对象,甚至是对象没有名称的情况.只有定义为类成员的对象才具有名称(出于显而易见的原因):在方法中构造的对象不具有名称.JVM有一个局部变量表,最多包含65535个条目.它们被加载到堆栈并通过*load和*store操作码存储到表中.

换句话说,一个类似的

public class Test {
int class_member = 42;

   public Test() {
      int my_local_field = 42;
   }
}
Run Code Online (Sandbox Code Playgroud)

被编译成

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--    
}
Run Code Online (Sandbox Code Playgroud)

在那里,你可以看到一个清晰的例子javap -v Test,当名称class_member被保留时,my_local_field已经被抽象为局部变量表中的索引1(0保留用于this).

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

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

其他人也是如此.

此外,大多数编译器默认情况下除了LineNumberTable之外不会产生任何东西,所以即使可能,你也会运气不好.

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

因此,在使用getFieldStrings时,您在可预见的未来会陷入困境.无耻的插件:IntelliJ似乎很好地处理具有反射的重构:编写Minecraft mods以及我知道这种感觉,并且可以说IntelliJ显着减少了重构工作.但对于大多数现代IDE来说也是如此:我确信大型玩家Eclipse,Netbeans 等.拥有良好实施的重构系统.


Ste*_*fan 9

字符串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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果将同一对象实例分配给多个字段,这将是不可靠的。 (2认同)

Ale*_*ien 7

这只能通过反射来实现.

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

  • 是的,但是使用反射我需要字符串中的字段名称作为参数(真实类中有更多字段,它们也有不同的类型).我的问题是,是否有一种方法可以从代码中的字段名称获取java.lang.reflect.Field类型的实例(不是来自字符串). (4认同)
  • @ShashiRanjan 尝试 *your-class-name*.class.getDeclaredFields()。这将返回 java.reflection.Field 类型的字段数组,您可以对其进行迭代并应用 .getName() 方法。 (2认同)

use*_*ser 7

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

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
Run Code Online (Sandbox Code Playgroud)

要获得字段的名称,请执行以下操作:

//use the field object from the example above
field.getName(); // it is in String.
Run Code Online (Sandbox Code Playgroud)

如果您不知道该字段的名称......那么..那么您希望得到什么字段?:)您可以使用反射获取所有字段然后循环它们.但是如果您不知道该字段,此操作中的实际意义和逻辑是什么?

  • 大量使用包含字段/类/方法/命名空间标识符的字符串的代码不易维护或重构(通常需要手动操作,IDE在此类问题上存在问题). (3认同)

Ogu*_*guz 6

我认为这就是您想要的。

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;
}
Run Code Online (Sandbox Code Playgroud)