Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic data type conversion method

Tags:

java

generics

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 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.

like image 873
Paul Bellora Avatar asked Aug 15 '11 04:08

Paul Bellora


People also ask

What are generic methods?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

What is generic type of data?

Definition: “A generic type is a generic class or interface that is parameterized over types.” Essentially, generic types allow you to write a general, generic class (or method) that works with different types, allowing for code re-use.

Why do we use generic data types?

Generics enable the use of stronger type-checking, the elimination of casts, and the ability to develop generic algorithms. Without generics, many of the features that we use in Java today would not be possible.

What is generic or parameterized data type?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


2 Answers

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.

like image 164
Bohemian Avatar answered Oct 14 '22 22:10

Bohemian


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]

like image 44
Edwin Dalorzo Avatar answered Oct 14 '22 23:10

Edwin Dalorzo