Geo*_*ich 5 java generics reflection casting hardcoded
在阅读了很多问题后,我问自己是否可以解决将字符串转换为通用数字而不使用硬编码方法的困境。
例如:我从方法中获取类型为 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) )
}
Run Code Online (Sandbox Code Playgroud)
甚至提出了这个问题:是否有可能将每个基元转换为与上述方式类似的东西?
注意:这里的方法应该完全关注非硬编码的解决方案。我当前运行的代码使用对每个案例进行硬编码的方法。但在这些情况下,更通用的解决方案会更有趣。
编辑: 我很抱歉造成误解,但我的意思是采用硬编码方法,这种方法可以测试每种可能的情况,例如:
if( integer ); do ...
if( double ); do ...
if( long ); do ...
Run Code Online (Sandbox Code Playgroud)
但这正是我想要解决的问题。澄清一下:这只是一个挑战。我的生活或代码并不依赖于它,我只是想知道它是否可能!
更新
由于原始答案中描述的方法(见下文)不支持基元,并且只能支持具有单个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());
}
Run Code Online (Sandbox Code Playgroud)
原答案
(Java 7)的 JavadocNumber列出了以下“直接已知子类”,以及显示的用于解析单个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)AtomicIntegerAtomicLong正如您所看到的,您最好使用带有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());
}
Run Code Online (Sandbox Code Playgroud)