So you have an array of objects. These objects are more than likely things that have properties. And life would be great if you could just get an array of all the values of just one of the properties of those objects.
Why would this be great? If these objects happen to map to a database instance (like entity beans), chances are each one of those objects has a long identifier, with a field probably named id
, with an accessor method getId
. My goal would be to do something like this:
List
Collection
As it turns out, this was not very difficult to pull off. And behold, there is something very similar in a project called the Java Generic Algorithms library. You can use the transform
method with the GetProperty
functor to achieve something very similar. The problem is that the GetProperty
doesn't work quite right. So I rolled my own.
One first part of the API is answering the question, "what should I call"? I am assuming the following things:
- You are thinking in terms of a field name, not the accessor function format, so I would always think "I'm getting the
id
" not "I'm getting theId
", and the case sensitivity matters. - There is likely a public accessor called
getFoo
, and iffoo
is a boolean, it might be calledgetFoo
, or it might be calledisFoo
. Depends. - If you didn't write an accessor, you're probably thinking this is some kind of C-style struct, so assume the field itself is accessable.
Here's my breakdown of this in a function I called reflectSimpleField
static privateT reflectSimpleField (Object object, String field, Class returnClass) throws NoSuchFieldException
{
Object reference = null;
Class klass = object.getClass();
// check for accessor method in the form getField or isField.
try
{
reference = reflectAccessorMethod(object, klass, field, "get");
}
catch (NoSuchMethodException ex)
{
try
{
reference = reflectAccessorMethod(object, klass, field, "is");
}
catch (NoSuchMethodException ex2)
{
reference = reflectField(object, klass, field);
}
}
return (T)reference;
}
Reflecting the individual methods and fields were pretty simple, you just have to figure out the name you're going to use, and if it isn't there, I decided I would throw NoSuchFieldException
. This isn't a RuntimeException
, which is a little weak, IMO. (In a production system, I might change that, but this is one of those coding excercises.) In general, there were too many damn exceptions being thrown anyway, which makes the API really messy:
static private Object reflectAccessorMethod (Object object, Class klass, String field, String accessor) throws NoSuchMethodException
{
Object reference = null;
try
{
String methodName = accessor + capitalize(field);
Method method = klass.getMethod(methodName, (Class[])null);
reference = method.invoke(object, (Object[])null);
}
catch (IllegalAccessException ex)
{
throw new NoSuchMethodException("rethrowing IllegalAccessException as NoSuchMethodException :" + ex.getMessage());
}
catch (InvocationTargetException ex)
{
throw new NoSuchMethodException("rethrowing InvocationTargetException as NoSuchMethodException :" + ex.getMessage());
}
return reference;
}
static private Object reflectField(Object object, Class klass, String fieldName) throws NoSuchFieldException
{
Object reference = null;
try
{
Field field = klass.getDeclaredField(fieldName);
reference = field.get(object);
}
catch (IllegalAccessException ex)
{
throw new NoSuchFieldException("rethrowing IllegalAccessException as NoSuchFieldException :" + ex.getMessage());
}
return reference;
}
private static String capitalize(String string)
{
return string.substring(0, 1).toUpperCase() + string.substring(1);
}
Finally, I came up with two variants of my original idea for collect
. The first, collect
was intended to also be usable in a for
loop, like so:
for (Long id : collect(objects, "id", Long.class))
{
// do something with id
}
This required adding the return type as an argument.
Just for kicks, I also came up with a specialized function for returning a List
instead of something Iterable
. This saves you from actually having to copy values just in case you don't want them.
My wack at these two functions:
public staticIterable collect (Collection collection, String field, Class returnClass) throws NoSuchFieldException
{
ArrayListlist = new ArrayList ();
for(T item : collection)
list.add(reflectSimpleField(item, field, returnClass));
return list;
}
public staticList collectList (Collection collection, String field) throws NoSuchFieldException
{
ClassreturnClass = null;
ArrayListlist = new ArrayList ();
for(T item : collection)
list.add(reflectSimpleField(item, field, returnClass));
return list;
}
If you find this stuff interesting or useful, feel free to cargo cult all you want. I'm curious to see how it compares with other things out there in the wild, and mostly, it was a great way to get used to some Java generics.
No comments:
Post a Comment