如何以通用方式处理数字?

我的问题非常类似于“编写一个通用类来处理内置类型”,包括受到在类处理矩阵操作的事实的启发。 虽然这个问题是用C#提出的,但指的是关于Generic Operators的文章。

我不明白。 Java Number没有add方法,因此您可以使用以下方法:

public Number myAdd(Number a, Number b){ return a.add(b); } 

那么你如何处理一个你希望能够在Java中处理多种类型的数字的情况?

基本问题是Java类型系统非常原始。

由于在Java中没有密集的类型集的概念(Java也不可能推断像Haskell那样的类型),所以没有办法使一般的数字+数字 – >数字没有欺骗。

对于原语(以及可以自动映射到它们的Integer等对象)类型提升和+操作是语言的一部分。 (这是问题的实际部分:数字a +数字b应该返回哪里a和b是不同类型的?)

如果你真的想要这种行为,你必须找到(或创建)你自己的自定义类,它使用reflection或一系列(检查和)强制转换等。 即使您使用generics(请记住generics是类型擦除的),也需要进行转换。

我想这些问题是Number为什么如此平淡无奇的原因之一。

你想要的结果有多好? 如果答案是“足够好,大多数”,那么这应该是足够的:

 public Number myAdd(Number a, Number b){ return a.doubleValue() + b.doubleValue(); } 

但是,如果你想要一些东西,比如说,与Java原语的推广语义相匹配,你可能不得不自己编写它。 然后你必须弄清楚“非标准” Number实现的所有组合的规则是什么,包括BigDecimalBigIntegerAtomicDoubleAtomicLongorg.apache.commons.lang.mutable所有内容,以及任何随机实现有人可能决定下周二写。

目前尚不清楚在大多数情况下做什么是正确的 – 例如,如果其中一个参数是Apache Commons的Fraction.ONE_THIRD ,那么将所有内容转换为BigDecimal都不是一个选项。 此外,以一般方式进行转换会产生与以一般方式进行添加相同的问题。 但是在Number上使用add()方法需要每个Number实现来处理所有这些情况 – 这可能就是为什么它不在那里。

我不明白。 Java Number没有添加方法……

假设java.lang.Number确实有一个或多个add方法,你会如何定义它的签名? 你会如何定义它的语义? 你会如何处理“混合模式”算术?

虽然毫无疑问可以回答这些问题并设计API,但结果可能很难正确使用。 此外,应用程序需要执行“表示不可知”算法是最不寻常的。 通常,您希望/需要显式控制执行算法和转换的方式。 (Java原始类型推广规则已经足够让人们了解!!)

总而言之,我认为Sun并没有尝试在Number API中支持算术,因此为我们提供了良好的服务

实现genericsadd方法的一种方法是让左手参数推断返回类型。

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

由于其他指出的原因,您不能添加任何两个数字,但您可以添加相同类型的数字,结果也将是相同的类型。 您可以使用Java创建generics算术,如下所示:

 interface Arithmetics { T add(T val1, T val2); } class IntegerArithmetics implements Arithmetics { Integer add(Integer val1, Integer val2) { return val1 + val2; } } //similarly DoubleArithmetics, BigIntegerArithmetics, ... 

通用Java Math库正是为您完成的。

实际上,我一直在研究一个通用的“真实”数字类(称为“价值”),但更多的是作为一种设计练习而不是任何东西; 而且我可以理解为什么它没有早点完成。

首先,你必须要有一些基本的规则来工作 – 我选择使用Java FP(IEEE-754)规则 – 这意味着你必须允许’无限’和’NaN’等结果,即使type实际上并不支持它们; 事实certificate,倒数等事情令人惊讶地棘手。 但是我到了那里,这是一段有趣的旅程。

有一件事对我有所帮助,我早就决定我需要处理“身份”值 – 特别是0,1和-1,以及-0,+ / – 无穷大和NaN作为特殊情况; 原因是(例如)乘以它们中的任何一个通常根本不需要任何计算。 x * 1 = x,x * NaN = NaN,x * 0 = 0(或NaN),x * +/-无穷大= +/-无穷大; 并且有分类,加法和减法的类似规则,这意味着您可以快速一致地消除大量的渣滓。 这使得实施者只需要处理需要计算的案例。

当然,并非所有类型都支持所有标识,但是如果你创建它们的方法,那么当操作数或结果“不受支持”时,你可以抛出exception。

希望它可以帮助其他任何有兴趣给它打击的人,但它看起来并不那么简单。 🙂

就个人而言,我几乎使用BigDecimals(但这主要是因为我使用货币值)。 它们处理任何大小的所有数值。 因此,在我看来,它们是一个通用值,可以在您的假设示例中使用,而不是使用Number抽象类。 一切都可以变成BigDecimal,为什么不用呢?

 public BigDecimal myAdd(BigDecimal a, BigDecimal b) { return a.add(b); } 

编辑:为了解决下面的BigBrothers评论,你总是可以使用doubleValue()方法来创建自己的generics方法。 唯一的问题是,在某些极端情况下,如果有人在BigDecimal中传入并且它大于Double.maxValue,则可能会丢失精度

 public Number myAdd(Number a, Number b) { return new BigDecimal(a.doubleValue() + b.doubleValue()); } 

BigDecimal是一个数字,所以返回一个是无关紧要的。