我想编写一个方法,它将泛型Number
作为参数并返回另一个Number
,其类型可能与第一个不同,并作为第二个参数传递。像这样:
public <N1 extends Number, N2 extends Number> N2 op(N1 num, Class<N2> retType) {
//Sample operation, where type conflicts arise.
return retType.cast(num + 3);
}
当然,如果他们是N1
和N2
,我就不能将Integer
投放到Long
。但是,Number
提供了doubleValue()
,intValue()
,...,这些方法可以让我通过switch / case语句部分解决问题。这会限制我xxxValue()
类公开的Number
方法的返回类型,从而切断AtomicInteger
,AtomicLong
,BigInteger
和{{1} }。
对于我想到的应用程序,这仍然是可以接受的,但是如果没有BigDecimal
语句,我的方法将无法正确处理Number
类的任何自定义或未来的官方扩展。 switch / case块应该随意决定调用哪个default
方法,或抛出异常(我想避免)。我可以使用枚举来按类型封装参数,但我担心我的代码会变得过于复杂和难以使用。
this question的答案提供了进一步的见解,即为两个参数声明一个通用类型(它不会强制两个参数在运行时实际上是相同的类型),这肯定是值得一提的是。
我想要实现的目标是:
xxxValue()
参数声明一个方法,可能有更多带有不同参数的方法(例如一个带有两个参数的方法,一个带有三个参数的方法)但在每一个中仍然是通用的。Number
的一些众所周知的扩展名(例如Number
,Integer
)。Double
,(Double, Integer)
,(Double, Double)
,(Integer, Double)
。我不想用不同的签名定义多个方法。即使在修复返回类型时,例如到(Integer, Integer)
时,随着更多参数和类型的添加,方法的数量会爆炸。
当然,我可能总是诉诸于每种方法的特定的临时实现,因为我可能不需要每种可能的类型组合。我仍然希望我的设计足够灵活,同时强制执行这些类型约束。
答案 0 :(得分:1)
这是我最好的解决方法,它调用接受String作为参数的构造函数,如果失败则返回null(编辑删除printStackTrace并删除一个不必要的分支)
public static <R extends Number> R op(Number num, Class<R> retType) {
BigDecimal three = BigDecimal.valueOf(3);
BigDecimal bdNum = new BigDecimal(num.toString());
//add three
BigDecimal bdResult = bdNum.add(three);
String strResult = bdResult.toString();
Constructor[] cons = retType.getDeclaredConstructors();
for (Constructor con: cons) {
if (con.getParameterCount() == 1) {
if (con.getGenericParameterTypes()[0] == String.class) {
try {
return (R)con.newInstance(strResult);
} catch (InstantiationException | IllegalAccessException | NumberFormatException e) {
} catch (InvocationTargetException e) {
//if here then either the decimal place is causing a problem
// when converting to integral type,
// or the value is too large for the target type
// so let's try to remove the decimal point by truncating it.
strResult = bdResult.toBigInteger().toString();
try {
return (R)con.newInstance(strResult);
} catch (NumberFormatException | IllegalAccessException | InstantiationException | InvocationTargetException e1) {
}
}
//if here, then the most likely the integral type is too large
//like trying to put 3,000,000,000 into an int
// when largest int possible is 2,147,483,647
}
//if here, then no constructors with 1 String parameter
}
}
return null;
}