If you are using Java 8, you can do:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
List<String> lines = new ArrayList<>(fields.length);
Arrays.stream(fields).forEach(field -> {
field.setAccessible(true);
try {
lines.add(field.getName() + " = " + field.get(this));
} catch (final IllegalAccessException e) {
lines.add(field.getName() + " > " + e.getClass().getSimpleName());
}
});
return lines.toArray(new String[lines.size()]);
}
or to improve formatting:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
List<String> lines = new ArrayList<>(fields.length);
Arrays.stream(fields)
.forEach(
field -> {
field.setAccessible(true);
try {
lines.add(field.getName() + " = " + field.get(this));
} catch (final IllegalAccessException e) {
lines.add(field.getName() + " > "
+ e.getClass().getSimpleName());
}
});
return lines.toArray(new String[lines.size()]);
}
Note that this is adding to @EricStein's excellent answer on normal concatenation instead of StringBuilders.
(I'm just starting to look into Streams, so if this is bad, please comment on why)
Otherwise...
Just a small comment:
lineBuilder.append(" = "); lineBuilder.append(value);
can easily be:
lineBuilder.append(" = ").append(value);
Same with:
lineBuilder.append(" > "); lineBuilder.append(e.getClass().getSimpleName());
becomes:
lineBuilder.append(" > ").append(e.getClass().getSimpleName());
Also, I would use a simple array instead of an ArrayList:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
String[] lines = new String[fields.length];
int index = 0;
for (Field field : fields) {
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.append(field.getName());
field.setAccessible(true);
try {
Object value = field.get(this);
lineBuilder.append(" = ");
lineBuilder.append(value);
} catch (IllegalAccessException e) {
lineBuilder.append(" > ");
lineBuilder.append(e.getClass().getSimpleName());
}
lines[index++] = lineBuilder.toString();
}
return lines;
}
EDIT: The above review, if not using Java 8, should be replaced by @EricStein's code.
Also, getFields() is a bad name. Try getFieldDescriptions().
If you are using Java 8, you can do:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
List<String> lines = new ArrayList<>(fields.length);
Arrays.stream(fields).forEach(field -> {
field.setAccessible(true);
try {
lines.add(field.getName() + " = " + field.get(this));
} catch (final IllegalAccessException e) {
lines.add(field.getName() + " > " + e.getClass().getSimpleName());
}
});
return lines.toArray(new String[lines.size()]);
}
or to improve formatting:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
List<String> lines = new ArrayList<>(fields.length);
Arrays.stream(fields)
.forEach(
field -> {
field.setAccessible(true);
try {
lines.add(field.getName() + " = " + field.get(this));
} catch (final IllegalAccessException e) {
lines.add(field.getName() + " > "
+ e.getClass().getSimpleName());
}
});
return lines.toArray(new String[lines.size()]);
}
Note that this is adding to @EricStein's excellent answer on normal concatenation instead of StringBuilders.
(I'm just starting to look into Streams, so if this is bad, please comment on why)
Otherwise...
Just a small comment:
lineBuilder.append(" = "); lineBuilder.append(value);
can easily be:
lineBuilder.append(" = ").append(value);
Same with:
lineBuilder.append(" > "); lineBuilder.append(e.getClass().getSimpleName());
becomes:
lineBuilder.append(" > ").append(e.getClass().getSimpleName());
Also, I would use a simple array instead of an ArrayList:
private String[] getFields() {
Class<? extends Component> componentClass = getClass();
Field[] fields = componentClass.getFields();
String[] lines = new String[fields.length];
int index = 0;
for (Field field : fields) {
StringBuilder lineBuilder = new StringBuilder();
lineBuilder.append(field.getName());
field.setAccessible(true);
try {
Object value = field.get(this);
lineBuilder.append(" = ");
lineBuilder.append(value);
} catch (IllegalAccessException e) {
lineBuilder.append(" > ");
lineBuilder.append(e.getClass().getSimpleName());
}
lines[index++] = lineBuilder.toString();
}
return lines;
}
EDIT: The above review, if not using Java 8, should be replaced by @EricStein's code.
Also, getFields() is a bad name. Try getFieldDescriptions().
For getFields(), String concatenation is a not-unreasonable alternative you can consider. For toString(), using delete() is probably easier to read than the conditional check, there's probably no real performance gain due to branch prediction. It is distinctly wrong to merge the two methods. getFields() is probably not the best name, since it doesn't actually return the fields. Good names are hard, and nothing is jumping immediately to mind.
public abstract class Component {
private String[] getFields() {
final List<String> lines = new ArrayList<>();
for (final Field field: this.getClass().getFields()) {
field.setAccessible(true);
try {
lines.add(field.getName() + " = " + field.get(this));
} catch (final IllegalAccessException e) {
lines.add(field.getName() + " > " + e.getClass().getSimpleName());
}
}
return lines.toArray(new String[lines.size()]);
}
@Override
public final String toString() {
final StringBuilder builder = new StringBuilder(this.getClass().getSimpleName());
builder.append('(');
for (final String field : this.getFields()) {
builder.append(field);
builder.append(", ");
}
builder.delete(builder.length() - 2, builder.length());
builder.append(')');
return builder.toString();
}
}
You can use Class#getDeclaredFields() to get all declared fields of the class. You can use Field#get() to get the value.
In short:
Object someObject = getItSomehow();
for (Field field : someObject.getClass().getDeclaredFields()) {
field.setAccessible(true); // You might want to set modifier to public first.
Object value = field.get(someObject);
if (value != null) {
System.out.println(field.getName() + "=" + value);
}
}
To learn more about reflection, check the Oracle tutorial on the subject.
That said, if that VO is a fullworthy Javabean, then the fields do not necessarily represent real properties of a VO. You would rather like to determine the public methods starting with get or is and then invoke it to grab the real property values.
for (Method method : someObject.getClass().getDeclaredMethods()) {
if (Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& method.getReturnType() != void.class
&& (method.getName().startsWith("get") || method.getName().startsWith("is"))
) {
Object value = method.invoke(someObject);
if (value != null) {
System.out.println(method.getName() + "=" + value);
}
}
}
That in turn said, there may be more elegant ways to solve your actual problem. If you elaborate a bit more about the functional requirement for which you think that this is the right solution, then we may be able to suggest the right solution. There are many, many tools available to massage javabeans. There's even a built-in one provided by Java SE in the java.beans package.
BeanInfo beanInfo = Introspector.getBeanInfo(someObject.getClass());
for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
Method getter = property.getReadMethod();
if (getter != null) {
Object value = getter.invoke(someObject);
if (value != null) {
System.out.println(property.getName() + "=" + value);
}
}
}
Here's a quick and dirty method that does what you want in a generic way. You'll need to add exception handling and you'll probably want to cache the BeanInfo types in a weakhashmap.
public Map<String, Object> getNonNullProperties(final Object thingy) {
final Map<String, Object> nonNullProperties = new TreeMap<String, Object>();
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(thingy
.getClass());
for (final PropertyDescriptor descriptor : beanInfo
.getPropertyDescriptors()) {
try {
final Object propertyValue = descriptor.getReadMethod()
.invoke(thingy);
if (propertyValue != null) {
nonNullProperties.put(descriptor.getName(),
propertyValue);
}
} catch (final IllegalArgumentException e) {
// handle this please
} catch (final IllegalAccessException e) {
// and this also
} catch (final InvocationTargetException e) {
// and this, too
}
}
} catch (final IntrospectionException e) {
// do something sensible here
}
return nonNullProperties;
}
See these references:
- BeanInfo (JavaDoc)
- Introspector.getBeanInfo(class) (JavaDoc)
- Introspection (Sun Java Tutorial)
obj = obj.getClass().getSuperclass().cast(obj);
This line does not do what you expect it to do. Casting an Object does not actually change it, it just tells the compiler to treat it as something else.
E.g. you can cast a List to a Collection, but it will still remain a List.
However, looping up through the super classes to access fields works fine without casting:
Class<?> current = yourClass;
while(current.getSuperclass()!=null){ // we don't want to process Object.class
// do something with current's fields
current = current.getSuperclass();
}
BTW, if you have access to the Spring Framework, there is a handy method for looping through the fields of a class and all super classes:
ReflectionUtils.doWithFields(baseClass, FieldCallback)
(also see this previous answer of mine: Access to private inherited fields via reflection in Java)
getDeclaredFields() gives you all fields on that Class, including private ones.
getFields() gives you all public fields on that Class AND it's superclasses.
If you want private / protected methods of Super Classes, you will have to repeatedly call getSuperclass() and then call getDeclaredFields() on the Super Class object.
Nothing here isn't clearly explained in the javadocs
Fortunately, you can do this using Java 8 - Streams
Assume you have an entity named YourEntity
public class YourEntity {
private String field1;
private String field2;
public YourEntity(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
public void setField1(String field1) {
this.field1 = field1;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
Declare you list of YourEntity using:
List<YourEntity> entities = Arrays.asList(new YourEntity("text1", "text2"), new YourEntity("text3", "text4"));
You can extract the list of field1 in one shot in this way:
import java.util.stream.Collectors;
List<String> field1List = entities.stream().map(YourEntity::getField1).collect(Collectors.toList());
Or in this way
import java.util.stream.Collectors;
List<String> field1List = entities.stream().map(urEntity -> urEntity.getField1()).collect(Collectors.toList());
You can print all the items also using java 8 :)
field1List.forEach(System.out::println);
Output
text1
text3
try this:
List<Entity> entities = getEntities();
List<Integer> listIntegerEntities = Lambda.extract(entities, Lambda.on(Entity.class).getFielf1());
the LambdaJ allows to access collections without explicit loops, so instead of have more lines of code to iterate the list yourself, you let LambdaJ do it.