通用数据类型转换方法

时间:2011-08-15 04:08:17

标签: java generics

此问题是针对opensas提出的另一个问题:building a generic initializer function in java

从他的问题来看,很明显他需要从任何数据类型T1转换为另一种类型T2。当我在这里说“数据类型”时,我指的是仅限于通常用于表示原始数据的类型:IntegerStringDate等。出于此问题的目的,我们可以考虑将原语装箱。

我想知道是否有任何API支持类型之间的转换,其中输入和输出都被推广到一组受支持的数据类型。我查看了Apache Commons' beanutils.converters package,但每个已知输入都有一个单独的转换器类。我正在寻找任何实现类似以下签名的功能:

static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);

或者

static <IN, OUT> OUT convert(IN value, OUT defaultValue);

使用指向各种Commons转换器的一堆else if块或者Map<Class<?>, Converter>用于同一目的,实现这种映射本身并不太难。但我想知道某种功能是否得到支持。

另外,如果结果重复,我道歉。我尝试找到类似的问题,当我发现没有一个符合这种情况时,我感到很惊讶。

编辑:因此,此代码的实例示例如下:

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

更新:我链接的BalusC代码接近标记,Bohemian的答案是一个很好的轻量级解决方案(虽然它不适用于布尔转换)。如果我们想要推广这些其他数据类型的转换,他也应该单独处理日期。我仍然希望得到更多的答案 - 特别是如果在某个地方有更多的免提API。

5 个答案:

答案 0 :(得分:20)

我不知道任何库,但代码只是一行。

除了Date之外,所有盒装基元都有一个String construtor,所以这个方法可以解决这个问题:

public static <I, O> O convert(I input, Class<O> outputClass) throws Exception {
    return input == null ? null : outputClass.getConstructor(String.class).newInstance(input.toString());
}

为了满足日期,你可以在方法中使用instanceof,但我会推荐一个单独的方法,因为转换日期是格式和上下文敏感的东西(例如字符串) - &gt;日期解析并使用哪种格式?,长 - >日期设置时间。

我故意将错误/特殊处理留给读者作为练习。

答案 1 :(得分:12)

JDK 8中,可以使用新的java.util.functions.Mapper界面和lambda expression轻松实现此目标。

Mapper<String,Integer> atoi = s -> Integer.valueOf(s);
Integer r = atoi.map("10");

使用方法引用它可以更简单:

Mapper<String, Integer> atoi = Integer::new;
Integer r = atoi.map("10");

或类似的事情:

List<Long> dates = asList(1344754620310L,1344754854877L);
List<Date> asDates = dates.map(Date::new).into(new ArrayList<Date>());

或酷转换如:

List<Integer> myInts = "5,4,3,2,1,0,6,7,8,9"
  .splitAsStream(",")
  .map(Integer::new)
  .into(new ArrayList<Integer>());

在JDK8 API的当前实现中,已经定义了一些默认映射器(即LongMapperIntMapperDoubleMapper),并且有一个名为{{的实用程序类1}}定义其他一些像字符串映射器,身份映射器,常量映射器等

我不确定这是否是你所追求的,但当然它必须是实现它的好方法。

像您建议的那样的案例:

Mappers

可以使用static <IN, OUT> OUT convert(IN value, Class<OUT> targetType); 实用程序类实现:

Mappers

你的签名:

Mapper<String, Integer> atoi = Mappers.instantiate(String.class, Integer.class);
Integer r = atoi.map("10");

可以通过以下方式实施:

static <IN, OUT> OUT convert(IN value, OUT default);

因此,像这样的代码...

Mapper<String, Integer> atoi = chain(substitute(null, "0"), Integer::new);
Integer r = atoi.map(null); //produces 0

会产生: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);

答案 2 :(得分:2)

查看Variance,它允许您设置一个类型转换上下文,其中注册了各种转换器,然后将值移入和移出Variant类型,并由上下文处理类型转换。

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()

转换器从一种类型到另一种类型只是Guava Functions,您可以注册自己的,覆盖所需的现有转化。

答案 3 :(得分:1)

我发现BalusC的内容与我要求的内容非常接近:http://balusc.blogspot.com/2007/08/generic-object-converter.html

不幸的是,不支持任何涉及日期转换的内容,但正如评论所示,可以轻松添加更多转换方法。他的类本质上是一个很好的小框架,它使用反射在运行时收集所有转换方法,并将它们放在HashMap<String, Method>中,其中键String是该输入输出组合的唯一ID。

仍在寻找其他建议!特别是对于一个比我链接的代码更容易接受的API。

答案 4 :(得分:1)

如果您使用的是Spring Framework(spring-core),则可以使用class

org.springframework.core.convert.support.DefaultConversionService

默认构造函数添加了许多类型转换器,您可以通过实现Converter接口并调用addConverter(Converter)来添加自己的转换器。还有很好的unit test显示了一些转换组合。