我的问题与"Writing a generic class to handle built-in types"非常类似,包括受到关于处理矩阵操作的类的启发。 虽然这个问题是用C#提出来的,并且指的是关于Generic Operators的文章。
我不明白。 Java Number没有add方法,因此您可以使用以下方法:
public Number myAdd(Number a, Number b){
return a.add(b);
}
那么你如何处理一个你希望能够在Java中处理多种类型数字的情况呢?
答案 0 :(得分:5)
基本问题在于Java类型系统非常原始。
由于在Java中没有密集的类型集的概念(Java也不可能推断像Haskell那样的类型),所以无法制作一般的数字+数字 - >没有诡计的数字。
对于原语(以及可以自动映射到它们的Integer等对象)类型提升和+操作是语言的一部分。 (这是问题的实际部分:数字a +数字b应该返回哪里a和b是不同类型的?)
如果你真的想要这种行为,你必须找到(或创建)你自己的自定义类,它使用反射或一系列(检查和)强制转换等。即使您使用泛型(请记住泛型是类型擦除的),也需要进行转换。
我想这些问题是数字为何如此平淡的原因的一部分。
答案 1 :(得分:3)
我不明白。 Java编号没有 有一个添加方法......
假设java.lang.Number
确实有add
个方法或方法,您将如何定义其签名?你会如何定义它的语义?你会如何处理“混合模式”算术?
虽然毫无疑问可以回答这些问题并设计API,但结果可能很难正确使用。此外,应用程序需要执行“表示不可知”算法是最不寻常的。通常,您希望/需要明确控制算法的执行方式和转换。 (Java原始类型推广规则已经足够让人们了解!!)
总而言之,我认为Sun已经
答案 2 :(得分:3)
您希望结果有多好?如果答案是“足够好,大多数”,那么这应该是足够的:
public Number myAdd(Number a, Number b){
return a.doubleValue() + b.doubleValue();
}
但是如果你想要一些东西,比如说,与Java原语的推广语义相匹配,你可能不得不自己编写它。然后,您必须弄清楚“非标准”Number
实施的所有组合的规则是什么,包括BigDecimal
,BigInteger
,AtomicDouble
,{{ 1}},AtomicLong
中的所有内容,以及下周二有人可能决定写的随机实现。
目前尚不清楚在大多数情况下做什么是正确的 - 例如,如果其中一个参数是Apache Commons'org.apache.commons.lang.mutable
,则将所有内容转换为BigDecimal
都不是一个选项。 ;此外,以一般方式进行转换会产生与以一般方式进行添加相同的问题。但是在Fraction.ONE_THIRD
上使用add()
方法需要每个Number
实现来处理所有这些情况 - 这可能就是为什么它不在那里。
答案 3 :(得分:1)
实现泛型add方法的一种方法是让左手参数推断返回类型。
package mixins;
import java.math.BigDecimal;
public class Numbers {
public static boolean isZ(Number n) {
return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte;
}
public static boolean isR(Number n) {
return n instanceof Double || n instanceof Float;
}
public static BigDecimal add(BigDecimal a, Number b) {
if (b instanceof BigDecimal) {
return a.add((BigDecimal) b);
} else if (isZ(b)) {
return a.add(new BigDecimal(b.longValue()));
} else if (isR(b)) {
return a.add(new BigDecimal(b.doubleValue()));
}
throw new IllegalArgumentException("No valid big decimal translation for " + b.getClass());
}
public static Integer add(Integer a, Number b) {
return a + b.intValue();
}
public static Long add(Long a, Number b) {
return a + b.longValue();
}
public static Float add(Float a, Number b) {
return a + b.floatValue();
}
public static Double add(Double a, Number b) {
return a + b.doubleValue();
}
}
如果将其作为静态方法实现,则可以使用静态导入。
import static mixins.Numbers.*;
public class Example {
public static void main(String[] args) {
BigDecimal fortytwo = new BigDecimal(42);
BigDecimal fiftyfive = add(fortytwo, 13);
System.out.println(fiftyfive);
}
}
答案 4 :(得分:0)
由于其他指出的原因,您无法添加任何两个数字,但您可以添加相同类型的数字,结果也将是相同的类型。 您可以使用Java创建泛型算术,如下所示:
interface Arithmetics<T> {
T add(T val1, T val2);
}
class IntegerArithmetics implements Arithmetics<Integer> {
Integer add(Integer val1, Integer val2) { return val1 + val2; }
}
//similarly DoubleArithmetics, BigIntegerArithmetics, ...
Generic Java Math库正是为您做的。
答案 5 :(得分:0)
实际上,我一直在研究一个通用的“真实”数字类(称为“价值”),但更多的是作为一种设计练习而不是任何东西;我明白为什么它没有早点完成。
首先,您必须要有一些基本规则才能使用 - 我选择使用Java FP(IEEE-754)规则 - 这意味着您必须允许“无限”和“NaN”等结果,即使该类型实际上不支持它们;事实证明,倒数等事情令人惊讶地棘手。但是我到了那里,这是一段有趣的旅程。
有一件事对我有所帮助,我早就决定我需要处理“身份”值 - 特别是0,1和-1,以及-0,+ / - 无限和NaN作为特殊情况;原因是(例如)乘以它们中的任何一个通常根本不需要任何计算。 x * 1 = x,x * NaN = NaN,x * 0 = 0(或NaN),x * +/-无穷大= +/-无穷大;分割,加法和减法有类似的规则,这意味着你可以快速一致地消除大量的渣滓。 这使实施者只需要处理做需要计算的案例。
当然,并非所有类型都支持所有标识,但是如果你创建它们的方法,那么当操作数或结果“不受支持”时,你可以抛出异常。
希望它可以帮助其他任何有兴趣给它打击的人,但它看起来并不那么简单。 : - )
答案 6 :(得分:-1)
就我个人而言,几乎所有东西都使用BigDecimals(但主要是因为我使用货币值)。它们处理任何大小的所有数值。因此,在我看来,它们是一个通用值,可以在您的假设示例中使用,而不是使用Number抽象类。 Everything 可以变成BigDecimal,为什么不使用呢?
public BigDecimal myAdd(BigDecimal a, BigDecimal b) {
return a.add(b);
}
编辑:要解决下面的BigBrothers评论,您总是可以使用doubleValue()方法来创建自己的通用方法。唯一的问题是,在某些极端情况下,如果有人在BigDecimal中传入并且它大于Double.maxValue
,则可能会丢失精度。public Number myAdd(Number a, Number b) {
return new BigDecimal(a.doubleValue() + b.doubleValue());
}
BigDecimal是一个数字,所以返回一个是无关紧要的。