I want to create a very generic utility method to take any Collection and convert it into a Collection of a user selectable class that extends from Number (Long, Double, Flo
I think the most important aspect of this code is the Function
as opposed to the method itself. I also don't think it makes sense to switch over the subclasses you allow in the Function
body, as you already know what type of Number
you want to return at the time the Function
is created. It's also slightly problematic that your method fails if given, say, BigInteger.class
.
Given this, what I would do is create a utility class (let's call it Numbers
) and provide methods on it that each return a Function
(which can be an enum
singleton) for parsing a String
as a specific type of Number
. That is:
public class Numbers {
public static Function<String, Integer> parseIntegerFunction() { ... }
public static Function<String, Long> parseLongFunction() { ... }
...
}
They could each be implemented something like this:
public static Function<String, Integer> parseIntegerFunction() {
return ParseIntegerFunction.INSTANCE;
}
private enum ParseIntegerFunction implements Function<String, Integer> {
INSTANCE;
public Integer apply(String input) {
return Integer.valueOf(input);
}
@Override public String toString() {
return "ParseIntegerFunction";
}
}
This can then be used however users want:
List<String> strings = ...
List<Integer> integers = Lists.transform(strings, Numbers.parseIntegerFunction());
This approach has quite a few advantages over yours:
Function
... we know what type of number we're creating and just do that. Faster.Function
can be used wherever... users aren't forced to use it the way your method does (copying the transformed values into an ImmutableList
.Function
s you actually want to allow. If there's no BigInteger
parsing function, users just can't call that, as opposed to having it be completely legal to do that at compile time and then fail at runtime like in your example.As a side note, I'd recommend making the return type of any method that returns an ImmutableList
be ImmutableList
rather than List
... it provides information that is useful to clients of the method.
Edit:
If you really need something more dynamic (i.e. you want classes that have an instance of some Class<T extends Number>
to be able to transform String
s to that Number
type) you could also add a lookup method like:
public static <T extends Number> Function<String, T> parseFunctionFor(Class<T> type) {
// lookup the function for the type in an ImmutableMap and return it
}
This has the same problems as your original method, though, if there's a Number
subclass that you don't provide a Function
for. It also doesn't seem like there would be many situations where this would be useful.
Why don't you implement several transformer functions and pass them to Lists.transform() call?
public class IntegerTransformer extends Function<String, Integer>() {
public Integer apply(String from) {
return Integer.valueOf(from);
}
}
So, you could write:
Lists.transform(stringValues, new IntegerTransformer());
If you want to handle types automatically, you can add a transformer factory or a map:
static Map<Class,Function<String,?>> transformers = new HashMap<String,?>();
static {
transformers.put(Integer.class, new IntegerTransformer());
transformers.put(Integer.class, new LongTransformer());
...
}
public static Function<String,?> get(Class c) {
Function<String,?> transformer = transformers.get(c);
if(transformer==null) {
throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
}
return transformer;
}
You could use reflection, and do something like this:
Method m = clazz.getDeclaredMethod("valueOf", String.class);
T str = (T) m.invoke(null, from);
return str;
Untested and possible slow.
Looks good to me.
Since you have the Class token, why not avoid the unchecked cast and thus suppress warnings?
retVal = clazz.cast(Double.valueOf(from));