开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜