Java code:
Transformer TRANSFORM_TO_INTEGER = new Transformer(开发者_如何学编程) {
public Object transform(Object input) {
Integer i = new Integer((String) input);
return i;
}
};
String begin = "1,2,3,4,5";
List strList = Arrays.asList(StringUtils.split(begin, ","));
CollectionUtils.transform(strList, TRANSFORM_TO_INTEGER);
This code would throw ArrayStoreException:
java.lang.ArrayStoreException
at java.util.Arrays$ArrayList.set(Arrays.java:2360)
at java.util.AbstractList$ListItr.set(AbstractList.java:488)
at org.apache.commons.collections.CollectionUtils.transform(CollectionUtils.java:434)
Why is that?
The ArrayStoreException
occurs when an attempt is made to store an object of an incorrect type is placed into an array.
What is the code doing?
In the example code given, the CollectionUtil.transform
method takes a Collection
and performs an in-place transform of the elements, which means that Object
s are taken out of the original Collection
(such as a List
) and placed back into the same Collection
.
The code for the Transformer
takes a String
and transforms it into a Integer
-- this is core issue here -- the type of object is changing when the transform is applied.
What could be going wrong?
As previously mentioned, CollectionUtil.transform
will use the given Transformer
and perform the transformation on each element in the Collection
and store it back to the original Collection
, which is the strList
.
I suspected that the List
created by Arrays.asList
is being backed by a String[]
, as that would be the likely be the source of the ArrayStoreException
. Running the debugger confirmed that, as it was backed by a String[5]
. (Using Eclipse, running on JRE 6 on Windows.)
What does the this example illustrate?
This is a prime example of how the lack of generics allows code that is not typesafe to be written, and consequently, a problem arises at runtime. If the code had been written with generics (and Apache Commons Collection supported it) these types of problems would be caught at compile time.
The bottom line -- one cannot transform type elements in a List
-- if the List
contains String
s, the Transformer.transform
should only return a String
.
What can be done?
As an alternative, Google Collections has a Collections2.transform
method, which takes a given Collection
and returns a Collection
transformed by a Function
.
This method supports generics, so it is typesafe, and the fact it returns a new Collection
means that the types can change through the transformation.
The Arrays.asList
method uses the same supplied array as the backing array for the new list instance. The API code looks like the following:
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
The call to StringUtils.split
creates a String[]
which is passed to the Arrays.asList
method. This would restrict the type of elements that you can insert into the new list instance to only String
objects.
CollectionUtils
class supports 2 different types of transformations:
In place transformation - In this case the input collection instance gets updated with the transformed values. All the
transform()
variants fall in this category. When using Collection types which are backed by arrays (e.g. ArrayList) the transformation can be successful only when the transformed values are type compatible with the backing array type. This explains the exception that you are seeing.Out of place transformation - In this case the input collection is never updated. Instead the transformed values are collected in a separate collection instance. All the
collect()
variants fall in this second category. The overloaded versions of collect() method either accepts the output collection as an argument or if no separate collection is specified creates a new list instance to collect the transformed values.
Based on the scenario that you are trying to address you should go with the 2nd type of transformation and call one of the collect()
variants.
精彩评论