将List <string>转换为List <integer>(或任何扩展Number的类)</integer> </string>

时间:2010-11-05 18:35:37

标签: java generics collections guava

我想创建一个非常通用的实用程序方法来获取任何Collection并将其转换为从Number(Long,Double,Float,Integer等)扩展的用户可选类的集合

我想出了这个代码,它使用Google Collections来转换Collection并返回一个不可变列表。

import java.util.List;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
     * Takes a {@code List<String>} and transforms it into a list of the
     * specified {@code clazz}.
     * 
     * @param <T>
     * @param stringValues
     *            the list of Strings to be used to create the list of the
     *            specified type
     * @param clazz
     *            must be a subclass of Number. Defines the type of the new List
     * @return
     */
    public static <T extends Number> List<T> toNumberList(List<String> stringValues, final Class<T> clazz) {
        List<T> ids = Lists.transform(stringValues, new Function<String, T>() {
            @SuppressWarnings("unchecked")
            @Override
            public T apply(String from) {
                T retVal = null;
                if (clazz.equals(Integer.class)) {
                    retVal = (T) Integer.valueOf(from);
                } else if (clazz.equals(Long.class)) {
                    retVal = (T) Long.valueOf(from);
                } else if (clazz.equals(Float.class)) {
                    retVal = (T) Float.valueOf(from);
                } else if (clazz.equals(Double.class)) {
                    retVal = (T) Double.valueOf(from);
                } else {
                    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
                }
                return retVal;
            }
        });
        return ImmutableList.copyOf(ids);
    }

可以像这样使用:

// Convert List<String> to List<Long>
List<Long> ids = MiscUtils.toNumberList(productIds, Long.class);

我的代码是否过度,或者您如何简化它并同时保持足够通用?

4 个答案:

答案 0 :(得分:7)

我认为此代码最重要的方面是Function而不是方法本身。我也不认为切换Function正文中允许的子类是有意义的,因为您已经知道Number时要返回的Function类型BigInteger.class被建造。如果给出了Numbers

,那么您的方法失败也会有些问题

鉴于此,我要做的是创建一个实用程序类(让我们称之为Function)并在其上提供每个都返回enum的方法(可以是String单例)用于将Number解析为特定类型的public class Numbers { public static Function<String, Integer> parseIntegerFunction() { ... } public static Function<String, Long> parseLongFunction() { ... } ... } 。那就是:

public static Function<String, Integer> parseIntegerFunction() {
  return ParseIntegerFunction.INSTANCE;
}

private enum ParseIntegerFunction implements Function<String, Integer> {
  INSTANCE;

  public Integer apply(String input) {
    return Integer.valueOf(input);
  }

  @Override public String toString() {
    return "ParseIntegerFunction";
  }
}

他们每个人都可以这样实现:

List<String> strings = ...
List<Integer> integers = Lists.transform(strings, Numbers.parseIntegerFunction());

然后可以根据用户的需要使用:

Function

这种方法比你的方法有很多优点:

  • Function中不需要任何切换...我们知道我们正在创建的是哪种类型的号码而且只是这样做。更快。
  • 更灵活,因为每个ImmutableList都可以在任何地方使用...用户不会像你的方法那样被迫使用它(将转换后的值复制到Function
  • 您只需创建您想要允许的BigInteger。如果没有ImmutableList解析函数,用户就不能调用它,而不是在编译时完全合法,然后在运行时失败,就像在你的例子中一样。

作为旁注,我建议将返回ImmutableList的任何方法的返回类型设为List而不是Class<T extends Number> ...它提供的信息对于该方法的客户。

修改

如果确实需要更动态的内容(即您希望具有某些String实例的类能够将Number转换为public static <T extends Number> Function<String, T> parseFunctionFor(Class<T> type) { // lookup the function for the type in an ImmutableMap and return it } 你也可以添加一个查找方法,如:

Number

这与原始方法存在同样的问题,但如果有Function子类,则不提供{{1}}。它似乎也不会有很多情况会有用。

答案 1 :(得分:2)

为什么不实现多个变换器函数并将它们传递给Lists.transform()调用?

    public class IntegerTransformer extends Function<String, Integer>() {
        public Integer apply(String from) {
            return Integer.valueOf(from);
        }
    }

所以,你可以写:

Lists.transform(stringValues, new IntegerTransformer());

如果要自动处理类型,可以添加变换器工厂或地图:

static Map<Class,Function<String,?>> transformers = new HashMap<String,?>();
static {
  transformers.put(Integer.class, new IntegerTransformer());
  transformers.put(Integer.class, new LongTransformer());
  ...
}

public static Function<String,?> get(Class c) {
  Function<String,?> transformer = transformers.get(c);
  if(transformer==null) {
    throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName()));
  }
  return transformer;         
}

答案 2 :(得分:0)

对我来说很好看。

由于您拥有类令牌,为什么不避免未经检查的强制转换,从而抑制警告?

retVal = clazz.cast(Double.valueOf(from)); 

答案 3 :(得分:-1)

您可以使用反射,并执行以下操作:

      Method m = clazz.getDeclaredMethod("valueOf", String.class);
      T str = (T) m.invoke(null, from);
      return str;

未经测试且可能很慢。