Reputation: 1
I'm trying to print public properties of an object which can be primitives or also an object. by using Reflection I manage to print the methods(getMethods())and primitive fields(getDeclaredFields()) but when I try to send recursively the field of type class(B) I get an error(in line printProperties(o);)- Exception in thread "main" java.lang.IllegalArgumentException: Can not set B field A.b1 to A
my problem is in -
else {
Object o = f.get(this);
printProperties(o);
How can I send a references of class B to printProperties in order to go into Class B and print all the properties their etc.. ?
example -
public class A {
public String s1;
public int 1;
public B b1;
}
public class B {
public String s2;
public int 2;
public C c1;
}
public void printProperties(Object reflectObject) throws IllegalAccessException {
Class reflectClass = (Class) reflectObject;
String className = reflectClass.getName();
System.out.println(className);
Method [] classMethod = reflectClass.getMethods();
for(Method m :classMethod){
System.out.println("Name "+m.getName());
System.out.println("Return "+m.getReturnType());
}
System.out.println("Fields");
Field[] fields = reflectClass.getDeclaredFields();
for (Field f : fields){
if(f.getType().isPrimitive()||f.getType().isAssignableFrom(String.class)){
System.out.println("Field "+f.getName());
}
else {
Object o = f.get(this);
printProperties(o);
}
Upvotes: 0
Views: 1158
Reputation: 298233
The problem of f.get(this)
is that the field’s declaring class in not compatible with the type of this
. Since you’re recursively traversing fields, you should use the field’s value instead. The next line printProperties(o);
suggests that you had such an approach in mind. But this contradicts the method’s first line Class reflectClass = (Class) reflectObject;
The field’s value is not a Class
object. But the fact that the first invocation did not fail with a ClassCastException
immediately but reach the get(this)
point suggests that the initial caller of this method did pass a Class
object rather than an actual instance.
It is possible to use the declared types only, without runtime objects. If you take this route, you should be consistent and declare the argument as Class
:
public void printProperties(Class<?> reflectClass) throws IllegalAccessException {
String className = reflectClass.getName();
System.out.println(className);
for(Method m: reflectClass.getMethods()) {
System.out.println("Name " + m.getName());
System.out.println("Return " + m.getReturnType());
}
System.out.println("Fields");
for(Field f: reflectClass.getDeclaredFields()) {
System.out.println("Field " + f.getName());
Class<?> fieldType = f.getType();
if(!fieldType.isPrimitive() && fieldType != String.class) {
printProperties(fieldType);
}
}
}
Then, you can invoke it with printProperties(getClass());
or printProperties(someObject.getClass());
or by specifying the type directly, e.g. printProperties(A.class);
.
The other approach is to use the actual value and its class. In this case, you have to fix the caller, to pass an object, like printProperties(this);
or printProperties(someObject);
. The compiler won’t warn you if you pass a Class
where an Object
is expected. In that case, you would be inspecting the class java.lang.Class
.
public void printProperties(Object reflectObject) throws IllegalAccessException {
Class<?> reflectClass = reflectObject.getClass();
String className = reflectClass.getName();
System.out.println(className);
for(Method m :reflectClass.getMethods()) {
System.out.println("Name " + m.getName());
System.out.println("Return " + m.getReturnType());
}
System.out.println("Fields");
for(Field f : reflectClass.getDeclaredFields()) {
System.out.println("Field " + f.getName());
Class<?> fieldType = f.getType();
if(!fieldType.isPrimitive() && fieldType != String.class) {
Object fieldValue = f.get(reflectObject);
if(fieldValue != null) printProperties(fieldValue);
}
}
}
This uses the actual value’s type. So when a field refers to an object of a subtype, the subtype will be inspected. On the other hand, this approach can’t inspect null
. You could combine both approaches, to use the field’s declared type when its value is null
. That is left as an exercise to the reader. When you are using both methods, it’s recommended to give them different names, to minimize the chances of mixing up Class
and Object
.
Upvotes: 4