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

时间:2009-09-01 00:25:31

标签: java generics

我的问题与"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中处理多种类型数字的情况呢?

7 个答案:

答案 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实施的所有组合的规则是什么,包括BigDecimalBigIntegerAtomicDouble,{{ 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是一个数字,所以返回一个是无关紧要的。