This question is in response to another question by opensas: building a generic initializer function in java
From his question it became clear that he needs to conve
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]
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.
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.