如何在 Java 中循环一个 Class 属性?

如何在 java 中动态地循环一个类属性。

例如:

public class MyClass{
private type1 att1;
private type2 att2;
...


public void function(){
for(var in MyClass.Attributes){
System.out.println(var.class);
}
}
}

这在 Java 中可行吗?

231805 次浏览

Java has Reflection (java.reflection.*), but I would suggest looking into a library like Apache Beanutils, it will make the process much less hairy than using reflection directly.

There is no linguistic support to do what you're asking for.

You can reflectively access the members of a type at run-time using reflection (e.g. with Class.getDeclaredFields() to get an array of Field), but depending on what you're trying to do, this may not be the best solution.

See also

Related questions


Example

Here's a simple example to show only some of what reflection is capable of doing.

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

The above snippet uses reflection to inspect all the declared fields of class String; it produces the following output:

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

Effective Java 2nd Edition, Item 53: Prefer interfaces to reflection

These are excerpts from the book:

Given a Class object, you can obtain Constructor, Method, and Field instances representing the constructors, methods and fields of the class. [They] let you manipulate their underlying counterparts reflectively. This power, however, comes at a price:

  • You lose all the benefits of compile-time checking.
  • The code required to perform reflective access is clumsy and verbose.
  • Performance suffers.

As a rule, objects should not be accessed reflectively in normal applications at runtime.

There are a few sophisticated applications that require reflection. Examples include [...omitted on purpose...] If you have any doubts as to whether your application falls into one of these categories, it probably doesn't.

Accessing the fields directly is not really good style in java. I would suggest creating getter and setter methods for the fields of your bean and then using then Introspector and BeanInfo classes from the java.beans package.

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

While I agree with Jörn's answer if your class conforms to the JavaBeabs spec, here is a good alternative if it doesn't and you use Spring.

Spring has a class named ReflectionUtils that offers some very powerful functionality, including doWithFields(class, callback), a visitor-style method that lets you iterate over a classes fields using a callback object like this:

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


});
}

But here's a warning: the class is labeled as "for internal use only", which is a pity if you ask me

Simple way to iterate over class fields and obtain values from object:

 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) {
}
}

Here is a solution which sorts the properties alphabetically and prints them all together with their values:

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