I'm glad that static typing is there to help... 13
The Background
A colleague was using FitNesse to create a general fixture for setting values in various objects rendered from a DTD. Of course you can write one per top level object, but given the number of eventual end-points, this would require a bit too much manual coding.This sounds like a candidate for reflection, correct? Yep, but rather than do that manually, using the Jakarta Commons BeanUtils makes sense – it’s a pretty handy library to be familiar with if you’re ever doing reflective programming with attributes.
package com.objectmentor.arraycopyexample; import static org.junit.Assert.assertEquals; import org.junit.Test; public class ArrayPropertySetterTest { @Test public void assertCanAssignToArrayFieldFromArrayOfObject() { Object[] arrayOfBars = createArrayOfBars(); Foo foo = new Foo(); ArrayPropertySetter.assignToArrayFieldFromObjectArray(foo, "bars", arrayOfBars); assertEquals(3, foo.getBars().length); } private Object[] createArrayOfBars() { Object[] objectArray = new Object[3]; for (int i = 0; i < objectArray.length; ++i) objectArray[i] = new Bar(); return objectArray; } }For completeness, you’ll need to see the Foo and Bar classes:
Bar
package com.objectmentor.arraycopyexample; public class Bar { }
Foo
package com.objectmentor.arraycopyexample; public class Foo { Bar[] bars; public Bar[] getBars() { return bars; } public void setBars(Bar[] bars) { this.bars = bars; } }
So an instance of a Foo holds on to an array of Bar objects; and the Foo class has the standard java-bean-esque setters and getters.
With this description of how to set an array field on a Java bean, let’s get this to actually work.
First question, how do you deal with arrays in Java? Sounds trivial, right. If you don’t mind a little pain, it’s not that bad… By dealing, I mean what happens when someone has given you an array created as follows:Object[] arrayOfObject = new Object[3]:Note that this is very different from this:
Object[] arrayOfBars = new Bar[3]:
The runtime type of these two results is different. One is array of Object; the other is Array of Bar.
This will not work:Bar[] arrayOfBar = (Bar[])arrayOfObject;This will generate a runtime cast exception. You cannot simply take something allocated as an array of objects and cast it to an array of a specific type. NO, you have to do something more like the following:
Array.newInstance(typeYouWantAnArrayOf, sizeOfArray);
That’s not too bad, right? You can then either use another method on the Array class to set the values, or you can cast the result to an appropriate array.
That’s enough information to write a generic method to copy from an array of Object to an array of a subtype of Object:public static Object[] copyToArrayOfType(Class destinationType, Object[] fromArray) { Object[] result = (Object[])Array.newInstance(destinationType, fromArray.length); for(int i = 0; i < fromArray.length; ++i) result[i] = fromArray[i]; return result; }This is a bit unruly because the caller still needs to cast the result:
Object[] arrayOfObject = new Object[] { new Foo(), new Foo(), new Foo() }; Foo[] arrayOfFoo = (Foo[])copyToArrayOfType(Foo.class, arrayOfObject);We can get rid of this cast if we use generics:
public static <T> T[] copyToArrayOfType(Class<T> destinationType, Object[] fromArray) { T[] result = (T[])Array.newInstance(destinationType, fromArray.length); for(int i = 0; i < fromArray.length; ++i) result[i] = (T) fromArray[i]; return result; }This doesn’t quite work because of type erasure, so to get this to “compile cleanly – no warnings”, you’ll need to add the following line above the method:
@SuppressWarnings("unchecked")
That’s just me telling the compiler I really think I know what I’m doing.
With this change, you can now write the following:Object[] arrayOfObject = new Object[] { new Foo(), new Foo(), new Foo() }; Foo[] arrayOfFoo = copyToArrayOfType(Foo.class, arrayOfObject);
The original problem was to take an array of Object[] and set it into a destination object’s attribute. Now we can create an array with the correct type, what next?
There are several things still remaining:- Given the name of the property, determine its underlying array type.
- Create the array (above)
- Assign the value to the underlying field
- Do some suficient hand-waving to handle exceptions
Here are solutions for each of those things:
Determine underlying type
PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor( destObject, fieldName); Class<?> destType = pd.getPropertyType().getComponentType();
Create the array
Object[] destArray = copyToArrayOfType.(destType, fromArray);
Assign the value
PropertyUtils.setSimpleProperty(destObject, fieldName, destArray);Here’s all of that put together and simply capturing all of the checked exceptions (that’s a whole other can of worms):
public static void assignToArrayFieldFromObjectArray(Object destObject, String fieldName, Object[] fromArray) { try { PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(destObject, fieldName); Class<?> destType = pd.getPropertyType().getComponentType(); Object[] destArray = copyToArrayOfType(destType, fromArray); PropertyUtils.setSimpleProperty(destObject, fieldName, destArray); } catch (Exception e) { throw new RuntimeException(e); } }
That’s all it takes to copy an array and then set the value in the field of a destination object.
Simple, right?
Sometimes static (and strong) typing can get in the way. This is one of those cases. Luckily, you can write this one and use it all over. Maybe it’s a part of the BeanUtils that I was unable to track down (probably).
You can eliminate one warning and push the suppression closer to the cause in copyToArrayOfType() like so:
Thanks Paul. For some reason, I had it in my mind that the Suppress Warnings annotation’s target was a method.
However, adding it to a declaration line is very smart. You’re cooking with gas!
Yeah, cool, that @SuppressWarnings…http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
Yeah, Java has a pretty sorry excuse for a static type system. The sort of thing that’d be a breeze in any modern language. (in many languages that predate Java 1.0, even)
It’s sorry to hear that Some white iPhone 4 buyers have reported signal reduction when the phone is held in certain ways, especially in the left hand, as the antenna problem is in the bottom left corner of the phone’s side casing. Is that mean i have to wait longer to get the white iphone 4?
So an instance of a Foo holds on to an array of Bar objects; and the Foo class has the standard java-bean-esque setters and getters.
nice and good sharing
nice and good sharing
Keep your Contacts and SMS safe! Actually, the contacts and SMS have more values than a cell phone’s own value. You can pay money to buy a new iPhone, but cannot buy your lost contacts and SMS back. So it’s important for you to backup your contacts and SMS in iPhone. And we recommend you backup contacts and SMS regularly. Our backup software can help you take a snapshot for your contacts and SMS. Your important personal information will be never lost.
woww very very nice.
internette görüntülü olarak okey oyunu oyna, gerçek kisilerle tanis, turnuva heyecanini yasa.
yeah Em glad Too
Static typing make work easy and relaible.Keep Posting Same Stuff in future
Your essay is good except for the spelling.
These beats by dr dre solo are just the same as selling in the franchise store.Or even better.