如何动态遍历Java中的类属性。
例如:
public class MyClass{
private type1 att1;
private type2 att2;
...
public void function(){
for(var in MyClass.Attributes){
System.out.println(var.class);
}
}
}
Java有可能吗?
Answers:
没有语言支持可满足您的要求。
您可以在运行时使用反射来反射地访问类型的成员(例如,使用Class.getDeclaredFields()
来获取的数组Field
),但是根据您要执行的操作,这可能不是最佳解决方案。
这是一个简单的示例,仅显示一些反射功能。
import java.lang.reflect.*;
public class DumpFields {
public static void main(String[] args) {
inspect(String.class);
}
static <T> void inspect(Class<T> klazz) {
Field[] fields = klazz.getDeclaredFields();
System.out.printf("%d fields:%n", fields.length);
for (Field field : fields) {
System.out.printf("%s %s %s%n",
Modifier.toString(field.getModifiers()),
field.getType().getSimpleName(),
field.getName()
);
}
}
}
上面的代码段使用反射来检查的所有声明的字段class String
;它产生以下输出:
7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER
这些是本书摘录:
给定一个
Class
对象,您可以获取Constructor
,Method
和Field
代表该类的构造函数,方法和字段的实例。[它们]使您可以反思地操纵其下层对等物。但是,此功能需要付出代价:
- 您将失去编译时检查的所有好处。
- 执行反射访问所需的代码笨拙且冗长。
- 性能受损。
通常,不应在运行时在普通应用程序中以反射方式访问对象。
有一些需要反射的复杂应用程序。示例包括[...故意省略...]如果您对您的应用程序是否属于以下类别之一有任何疑问,则可能不会。
Field.get
,你可以用它来反射性地读取一个字段的值。如果是这样private
,您也许可以通过解决该问题setAccessible
。是的,反射功能非常强大,它可以让您执行诸如检查private
字段,覆盖final
字段,调用private
构造函数等操作。如果您在设计方面需要进一步的帮助,则可能应该编写一个包含更多信息的新问题。也许您一开始不需要这么多属性(例如useenum
或List
等)。
static void inspect(Class<?> klazz)
在Java中,直接访问字段并不是真正好的样式。我建议为bean的字段创建getter和setter方法,然后使用java.beans包中的Introspector和BeanInfo类。
MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
String propertyName = propertyDesc.getName();
Object value = propertyDesc.getReadMethod().invoke(bean);
}
propertyDesc.getReadMethod().getDeclaringClass() != Object.class
,也可以指定一个类来停止分析,如作为第二个参数getBeanInfo(MyBean.class, Object.class)
。
如果您的课程符合JavaBeabs规范,虽然我同意Jörn的回答,但如果不符合JavaBeabs规范,这是一个不错的选择,并且您使用Spring。
Spring有一个名为ReflectionUtils的类,它提供了一些非常强大的功能,包括doWithFields(class,callback),这是一种访问者风格的方法,可让您使用这样的回调对象遍历类字段:
public void analyze(Object obj){
ReflectionUtils.doWithFields(obj.getClass(), field -> {
System.out.println("Field name: " + field.getName());
field.setAccessible(true);
System.out.println("Field value: "+ field.get(obj));
});
}
但这是一个警告:该类被标记为“仅供内部使用”,如果您问我,这是一个遗憾
遍历类字段并从对象获取值的简单方法:
Class<?> c = obj.getClass();
Field[] fields = c.getDeclaredFields();
Map<String, Object> temp = new HashMap<String, Object>();
for( Field field : fields ){
try {
temp.put(field.getName().toString(), field.get(obj));
} catch (IllegalArgumentException e1) {
} catch (IllegalAccessException e1) {
}
}
Java具有反射(java.reflection。*),但我建议您研究一下类似Apache Beanutils的库,它比直接使用反射要少得多。
这是一个解决方案,可以按字母顺序对属性进行排序,并将所有属性及其值一起打印:
public void logProperties() throws IllegalArgumentException, IllegalAccessException {
Class<?> aClass = this.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
Map<String, String> logEntries = new HashMap<>();
for (Field field : declaredFields) {
field.setAccessible(true);
Object[] arguments = new Object[]{
field.getName(),
field.getType().getSimpleName(),
String.valueOf(field.get(this))
};
String template = "- Property: {0} (Type: {1}, Value: {2})";
String logMessage = System.getProperty("line.separator")
+ MessageFormat.format(template, arguments);
logEntries.put(field.getName(), logMessage);
}
SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());
StringBuilder sb = new StringBuilder("Class properties:");
Iterator<String> it = sortedLog.iterator();
while (it.hasNext()) {
String key = it.next();
sb.append(logEntries.get(key));
}
System.out.println(sb.toString());
}