Java为java.lang.Number或原始

时间:2016-04-02 01:35:55

标签: java generics reflection casting hardcoded

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

例如:我从一个方法得到一个类型为Class的参数 使用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 ...

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

3 个答案:

答案 0 :(得分:2)

<强>更新

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

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

正如您所看到的,即使是原始值也可以使用适当的解析方法来处理,但是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());
}

原始回答

Number的Javadoc(Java 7)列出了以下&#34;直接已知的子类&#34;,以及用于解析单个String参数的所示方法:

正如您所看到的,您最好使用带有String参数的构造函数。这种方式也会得到BigDecimalBigInteger的支持。

现在,至于如何。使用反射。你有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());
}

答案 1 :(得分:0)

  1. 使用反射(example),尝试类路径中存在的所有已知的Number(Double,Integer等)实现。
  2. 对于每一个,尝试静态方法valueOf(String)和String构造函数(没有在Number接口中)。
  3. 对于每个Number实例,请确保您可以获得与用户输入等效的String表示形式。否则你可能会出现整数溢出。

答案 2 :(得分:0)

此答案将@Andreas提供的答案扩展为利用IntegerShortByte 使用的静态缓存(请参阅{{ 3}}了解详情)。这是可能的,因为每个类提供的静态工厂方法valueOf(String)。例如,默认情况下,Integer会缓存-128到127之间的所有值(以及此范围this answer)。

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