在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,但谁知道..)
答案 0 :(得分:13)
不幸的是,没有办法做你想做的事。 为什么?
在我们仍在努力证明Java不慢的时代,存储构造对象的位置或方式的元数据会产生很大的开销,并且因为它的使用范围非常小,所以它不存在。不仅如此:还有许多技术原因。
一个原因是因为JVM的实现方式。可以找到Java class
文件格式的规范here。这是非常满口的,但它非常有用。
正如我之前所说,可以在任何地方构建对象,即使对象没有名称的情况也是如此。只有定义为类成员的对象才具有名称(出于显而易见的原因):在方法中构造的对象不具有名称。 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
使用字符串会让您陷入困境。无耻的插件:IntelliJ似乎很好地处理了具有反射的重构:编写Minecraft mods以及我知道这种感觉,并且可以说IntelliJ会大大减少重构工作。但对于大多数现代IDE来说也是如此:我确信Eclipse,Netbeans 等这些大型企业已经实施了良好的重构系统。
答案 1 :(得分: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
要获得字段名称,请执行以下操作:
//use the field object from the example above
field.getName(); // it is in String.
如果您不知道该字段的名称......那么......那么您希望获得哪个字段? :)您可以使用反射获取所有字段然后循环它们。但是如果您不知道该字段,此操作中的实际意义和逻辑是什么?
答案 2 :(得分:6)
这只能通过反思来实现。
使用Class.getDeclaringFields
()
和java.reflection.Field.getName()
答案 3 :(得分:5)
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;
}
答案 4 :(得分:4)
我认为这就是你要找的东西。
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;
}
答案 5 :(得分:0)
可以使用运行时字节代码检测来完成,例如使用Byte Buddy库,请参阅此答案nameof equivalent in Java
答案 6 :(得分:0)
如何使用 lombok 注解 @FieldNameConstants
然后您可以通过以下方式访问您的字段名称:
mod.Field.linkTool
请注意,此功能被标记为实验性。