Generic data type conversion method
This question is in response to another questi开发者_高级运维on by opensas: building a generic initializer function in java
From his question it became clear that he needs to convert from any data type T1
to another type T2
. When I say "data type" here, I mean types limited to those commonly used to represent raw data: Integer
, String
, Date
, etc. For the purpose of this question we can consider primitives to be boxed.
I'm wondering if there is any API that supports conversion between types where both the input and output are generalized to a set of supported data types. I had a look at Apache Commons' beanutils.converters package, but there's a separate converter class for each known input. I'm looking for any functionality that implements something like the following signature:
static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);
or else
static <IN, OUT> OUT convert(IN value, OUT defaultValue);
It really wouldn't be too hard to implement this kind of mapping oneself, either using a bunch of else if
blocks pointing to the various Commons Converters, or else a Map<Class<?>, Converter>
for the same purpose. But I'm wondering if this kind of functionality is supported somewhere.
Also, if this winds up being a duplicate I apologize. I tried finding similar questions and was surprised when I found none matching this situation.
EDIT: so an example of this code in action would be:
Integer i = GenericConverter.convert("123", Integer.class); //returns 123
Date d = GenericConverter.convert(1313381772316L, Date.class); //returns today's date
Boolean b = GenericConverter.convert(0, Boolean.class); //returns false
Long l = GenericConverter.convert("asdf", Long.class); //RuntimeException
UPDATE: The BalusC code I linked falls close to the mark, and Bohemian's answer is a nice lightweight solution (although it doesn't work for Boolean conversions). He's also right that Dates should be probably be handled separately if we want to generalize conversion of these other data types. I'm still hoping for additional answers though - especially if there is more of a hands-off API available somewhere.
I am not aware of any library, however the code is just one line.
Apart from Date, all boxed primitives have a String construtor, so this method does the trick:
public static <I, O> O convert(I input, Class<O> outputClass) throws Exception {
return input == null ? null : outputClass.getConstructor(String.class).newInstance(input.toString());
}
To cater for Dates, you could use instanceof
within the method, but I would recommend a separate method, since converting dates is a format- and context-sensitive thing (eg String-->Date parses and uses which format?, Long-->Date sets the time).
I have deliberately left error/special handling to the reader as an exercise.
In JDK 8 this can be easily implemented with the new java.util.functions.Mapper
interface and a lambda expression.
Mapper<String,Integer> atoi = s -> Integer.valueOf(s);
Integer r = atoi.map("10");
Using method references it can be even simpler:
Mapper<String, Integer> atoi = Integer::new;
Integer r = atoi.map("10");
Or things like:
List<Long> dates = asList(1344754620310L,1344754854877L);
List<Date> asDates = dates.map(Date::new).into(new ArrayList<Date>());
Or cool conversions like:
List<Integer> myInts = "5,4,3,2,1,0,6,7,8,9"
.splitAsStream(",")
.map(Integer::new)
.into(new ArrayList<Integer>());
In the current implementation of the JDK8 API, a few default mappers have been defined (i.e. LongMapper
, IntMapper
, DoubleMapper
) and there's a utility class called Mappers
that defines some others like a string mapper, and identity mapper, a constant mapper, etc.
I am not sure if this is what you are after, but certainly it must be a nice way to implement it.
Cases like the one you suggest for:
static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);
Can be implemented with the Mappers
utility class:
Mapper<String, Integer> atoi = Mappers.instantiate(String.class, Integer.class);
Integer r = atoi.map("10");
And your signature:
static <IN, OUT> OUT convert(IN value, OUT default);
Could be implemented with something like:
Mapper<String, Integer> atoi = chain(substitute(null, "0"), Integer::new);
Integer r = atoi.map(null); //produces 0
As such, a code like this...
List<String> data = asList("0", null, "2", null, "4", null, "6");
List<Integer> myInts = data.map(chain(substitute(null, "0"), Integer::new)).into(new ArrayList<Integer>());
System.out.println(myInts);
Would yield: [0, 0, 2, 0, 4, 0, 6]
If you are using Spring Framework (spring-core), you can use class
org.springframework.core.convert.support.DefaultConversionService
Default constructor adds many types converter and you can add your own by implementing Converter interface and call addConverter(Converter). There is also nice unit test showing some conversion combinations.
I found something by BalusC that looks close to what I'm asking for: http://balusc.blogspot.com/2007/08/generic-object-converter.html
Unfortunately nothing involving Date conversion is supported, but as the comments indicate, more conversion methods are easily added. His class is essentially a nice little framework that uses reflection to gather up all conversion methods at runtime and put them in a HashMap<String, Method>
where the key String
is a unique id for that input-output combination.
Still looking for other suggestions! Particularly for an API that would be more hands off than this code I linked to.
Take a look at Variance, which allows you to set up a type conversion context with various converters registered and then move values into and out of a Variant type with type conversion handled by the context.
Variant aVariant = Variant.of("1.2345");
double aDouble = aVariant.doubleValue();
int anInt = Variant.of("12").intValue();
String aString = Variant.of(12.0).toString();
Date aDate = Variant.of("2012-04-06").as(Date.class);
String anIsoFormattedDate = Variant.of(aDate).in(isoDateFormattingContext).toString()
Converters are just Guava Functions from one type to another, and you can register your own, overriding existing conversions where desired.
精彩评论