将List 转换为List (或任何扩展Number的类)
我想创建一个非常通用的实用工具方法来获取任何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} and transforms it into a list of the * specified {@code clazz}. * * @param * @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 List toNumberList(List stringValues, final Class clazz) { List ids = Lists.transform(stringValues, new Function() { @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 to List List ids = MiscUtils.toNumberList(productIds, Long.class);
我的代码是否过度杀伤,或者您如何简化它并同时保持足够的通用性?
我认为这个代码最重要的方面是Function
,而不是方法本身。 我也不认为在Function
体中切换允许的子类是有意义的,因为您已经知道在创建Function
时要返回的Number
类型。 如果给出BigInteger.class
,那么你的方法失败也有些问题。
鉴于此,我要做的是创建一个实用程序类(让我们称之为Numbers
)并在其上提供方法,每个方法都返回一个Function
(可以是一个enum
单例),用于将String
解析为特定类型的Number
。 那是:
public class Numbers { public static Function parseIntegerFunction() { ... } public static Function parseLongFunction() { ... } ... }
他们每个都可以这样实现:
public static Function parseIntegerFunction() { return ParseIntegerFunction.INSTANCE; } private enum ParseIntegerFunction implements Function { INSTANCE; public Integer apply(String input) { return Integer.valueOf(input); } @Override public String toString() { return "ParseIntegerFunction"; } }
然后可以使用它,但用户需要:
List strings = ... List integers = Lists.transform(strings, Numbers.parseIntegerFunction());
这种方法比你的方法有很多优点:
- 不需要在
Function
进行任何切换……我们知道我们正在创建的数字类型,并且就是这样做的。 快点。 - 更灵活,因为每个
Function
都可以在任何地方使用…用户不必像你的方法那样使用它(将转换后的值复制到ImmutableList
。 - 您只需创建您想要允许的function。 如果没有
BigInteger
解析函数,用户就不能调用它,而不是在编译时完全合法,然后在运行时失败,就像在你的例子中一样。
作为旁注,我建议将返回ImmutableList
的任何方法的返回类型设置为ImmutableList
而不是List
…它提供对该方法的客户端有用的信息。
编辑:
如果你真的需要一些更动态的东西(即你希望类具有某个Class
的实例能够将String
转换为该Number
类型),你还可以添加一个查找方法,如:
public static Function parseFunctionFor(Class type) { // lookup the function for the type in an ImmutableMap and return it }
这与原始方法有相同的问题,但是,如果有一个Number
子类,则不提供Function
。 它似乎也不会有很多情况会有用。
为什么不实现几个变换器函数并将它们传递给Lists.transform()调用?
public class IntegerTransformer extends Function() { public Integer apply(String from) { return Integer.valueOf(from); } }
所以,你可以写:
Lists.transform(stringValues, new IntegerTransformer());
如果要自动处理类型,可以添加变换器工厂或地图:
static Map> transformers = new HashMap(); static { transformers.put(Integer.class, new IntegerTransformer()); transformers.put(Integer.class, new LongTransformer()); ... } public static Function get(Class c) { Function transformer = transformers.get(c); if(transformer==null) { throw new RuntimeException(String.format("Type %s is not supported (yet)", clazz.getName())); } return transformer; }
在我看来很好。
既然你有类令牌,为什么不避免未经检查的强制转换,从而抑制警告?
retVal = clazz.cast(Double.valueOf(from));
你可以使用reflection,并做这样的事情:
Method m = clazz.getDeclaredMethod("valueOf", String.class); T str = (T) m.invoke(null, from); return str;
未经测试,可能很慢。