在阅读了很多问题后,我问自己是否有可能解决将字符串转换为通用数字而无需使用硬编码方法的困境。
例如:我从一个方法得到一个类型为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 ...
但这正是我想要解决的问题。澄清:这只是一个挑战。我的生活或代码不依赖于它,我只想知道它是否可能!!
答案 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
参数的所示方法:
Byte
,new Byte(String s)
,valueOf(String s)
,decode(String nm)
Short
,new Short(String s)
,valueOf(String s)
,decode(String nm)
Integer
,new Integer(String s)
,valueOf(String s)
,decode(String nm)
Long
,new Long(String s)
,valueOf(String s)
,decode(String nm)
Double
,new Double(String s)
,valueOf(String s)
Float
,new Float(String s)
,valueOf(String s)
BigDecimal
,new BigDecimal(String val)
BigInteger
,new BigInteger(String val)
AtomicInteger
AtomicLong
正如您所看到的,您最好使用带有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());
}
答案 1 :(得分:0)
Number
接口中)。答案 2 :(得分:0)
此答案将@Andreas提供的答案扩展为利用Integer
,Short
和Byte
使用的静态缓存(请参阅{{ 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());
}
}