【问题标题】:Generic data type conversion method通用数据类型转换方法
【发布时间】:2017-03-17 18:27:23
【问题描述】:

这个问题是对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&lt;Class&lt;?&gt;, Converter&gt; 用于相同目的。但我想知道某处是否支持这种功能。

另外,如果这最终成为重复,我深表歉意。我尝试寻找类似的问题,但当我发现没有一个符合这种情况时,我感到很惊讶。

编辑:所以这个代码的一个例子是:

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 可用。

【问题讨论】:

  • 你能展示这段代码吗?不能说我真的能理解你的目标是什么。
  • @Maurício - 添加了一些示例

标签: java generics


【解决方案1】:

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

除了 Date,所有装箱的原语都有一个 String 构造函数,所以这个方法可以解决问题:

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,但我会推荐一个单独的方法,因为转换日期是一种格式和上下文敏感的事情(例如字符串-- >Date解析并使用哪种格式?,Long-->Date设置时间)。

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

【讨论】:

  • +1 这很酷,没有意识到所有盒装原语都有 String 构造函数。不支持某些转换,例如Boolean=>Integer,但这又是一个极端情况。正如您所指出的,这不考虑日期(或其他潜在的用户定义的“数据类型”)
  • 另外:“我故意将错误/特殊处理留给读者作为练习。”我知道你只是想把它放在一条线上:)
  • type I 在这里是不必要的;它可能只是Object
  • @user102008 可能是这样,但问题有那个签名
【解决方案2】:

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),还有一个名为 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);

将产生:[0, 0, 2, 0, 4, 0, 6]

【讨论】:

  • +1 很好的答案 - 我想在 JDK 8 全面上市之前我会接受 Dominic 的。
  • 现在是 2016 年,没有 java.util.functions.Mapper 但有 java.util.function.Function
【解决方案3】:

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

org.springframework.core.convert.support.DefaultConversionService

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

【讨论】:

    【解决方案4】:

    我发现 BalusC 的一些东西看起来与我的要求很接近:http://balusc.blogspot.com/2007/08/generic-object-converter.html

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

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

    【讨论】:

      【解决方案5】:

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

      转换器只是从一种类型到另一种类型的GuavaFunctions,您可以注册自己的,在需要时覆盖现有的转换。

      【讨论】:

      • 很遗憾,这不在 Maven 存储库中。
      猜你喜欢
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-04
      • 1970-01-01
      相关资源
      最近更新 更多