4

在阅读了很多问题之后,我问自己是否有可能在不使用硬编码方法的情况下解决将字符串转换为通用数字的困境。

例如:我从方法中获取类型为 Class With Number.isAssignableFrom 的参数或我可以检查的其他方式,如果这是一个数字类。但我也从用户那里得到一个输入。作为一个字符串。

问题是:我现在可以以某种方式将此字符串转换为请求的数字对象,而无需为每种情况构建一个 if 语句吗?

示例代码,不能正常工作:

Object ret = null;

for(int i=0;i<method.getParameterTypes().length; i++ ) {
    Class<?> param = method.getParameterTypes()[i];
    String argString = getUserInput(_in, "Argument ("+param.getSimpleName()+"): ");

    if( Number.isAssignableFrom(param) )
        ret = ((Class<NumberChild>)param).valueOf(argString);
    /// OR
        ret = param.cast( Double.valueOf(argString) )
}

甚至提出这个问题:是否可以从上述方式以类似的方式转换每个原语?

注意:这里的方法应该完全专注于非硬编码的解决方案。我当前运行的代码使用硬编码每个案例的方法。但在这些情况下,更通用的解决方案会更有趣。

编辑: 对于误解,我很抱歉,但我的意思是使用硬编码的方法,这种方法可以测试所有可能的情况,例如:

 if( integer ); do ...
 if( double ); do ...
 if( long ); do ...

但这正是我想要解决的问题。澄清一下:这只是一个挑战。我的生活或代码不依赖于它,我只想知道它是否可能!

4

3 回答 3

3

更新

由于原始答案中描述的方法(见下文)不支持原语,并且只能支持具有单个String参数的构造函数的类,因此最好为每个类显式指定要使用的解析器方法。

使用 Java 8 方法引用,这变得容易得多。

如您所见,即使是原始值也可以使用适当的 parse 方法进行处理,但是parse()这里的方法仍然返回Object,因此任何原始值仍然被装箱。通过反射处理基元时通常是这种情况。

这是一个简化的例子。有关完整的工作示例,请参见IDEONE

private static HashMap<Class<?>, Function<String,?>> parser = new HashMap<>();
static {
    parser.put(boolean.class   , Boolean::parseBoolean); // Support boolean literals too
    parser.put(int.class       , Integer::parseInt);
    parser.put(long.class      , Long::parseLong);
    parser.put(Boolean.class   , Boolean::valueOf);
    parser.put(Integer.class   , Integer::valueOf);
    parser.put(Long.class      , Long::valueOf);
    parser.put(Double.class    , Double::valueOf);
    parser.put(Float.class     , Float::valueOf);
    parser.put(String.class    , String::valueOf);  // Handle String without special test
    parser.put(BigDecimal.class, BigDecimal::new);
    parser.put(BigInteger.class, BigInteger::new);
    parser.put(LocalDate.class , LocalDate::parse); // Java 8 time API
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private static Object parse(String argString, Class param) {
    Function<String,?> func = parser.get(param);
    if (func != null)
        return func.apply(argString);
    if (param.isEnum()) // Special handling for enums
        return Enum.valueOf(param, argString);
    throw new UnsupportedOperationException("Cannot parse string to " + param.getName());
}

原始答案

Javadoc for Number(Java 7) 列出了以下“直接已知的子类”,以及解析单个String参数的所示方法:

如您所见,最好使用带String参数的构造函数。BigDecimal这种方式也将得到支持BigInteger

现在,至于如何。使用反射。你有Class, 所以向它询问构造函数,然后调用它。

Class param = /*code here*/;
String argString = /*code here*/;

Object ret;
try {
    Constructor ctor = param.getConstructor(String.class);
    ret = ctor.newInstance(argString);
} catch (ReflectiveOperationException e) {
    throw new UnsupportedOperationException("Cannot convert string to " + param.getName());
}
于 2016-04-02T02:22:32.523 回答
1

此答案扩展了@Andreas 提供的答案,以利用 、 和 使用的静态缓存IntegerShort有关Byte详细信息,请参阅此答案)。valueOf(String)这是可能的,因为这些类中的每一个都提供了静态工厂方法。例如,默认情况下,Integer缓存 -128 和 127 之间的所有值(并且可以使用-XX:AutoBoxCacheMaxJVM 选项扩展此范围)。

public static Number asNumber(String str,
        Class<? extends Number> param) throws UnsupportedOperationException {
    try {
        /*
         * Try to access the staticFactory method for: 
         * Byte, Short, Integer, Long, Double, and Float
         */
        Method m = param.getMethod("valueOf", String.class);
        Object o = m.invoke(param, str);
        return param.cast(o);
    } catch (NoSuchMethodException e1) {
        /* Try to access the constructor for BigDecimal or BigInteger*/
        try {
            Constructor<? extends Number> ctor = param
                    .getConstructor(String.class);
            return ctor.newInstance(str);
        } catch (ReflectiveOperationException e2) {
            /* AtomicInteger and AtomicLong not supported */
            throw new UnsupportedOperationException(
                    "Cannot convert string to " + param.getName());
        }
    } catch (ReflectiveOperationException e2) {
        throw new UnsupportedOperationException("Cannot convert string to "
                + param.getName());
    }   
}
于 2016-04-02T02:56:49.807 回答
0
  1. 使用反射(示例),尝试类路径中存在的所有已知的数字(双精度、整数等)实现。
  2. 对于每一个,尝试静态方法 valueOf(String) 和 String 构造函数(Number接口中都没有)。
  3. 对于每个 Number 实例,确保您可以获得与用户输入等效的 String 表示形式。否则你可能会得到整数溢出。
于 2016-04-02T02:16:10.853 回答