【问题标题】:Is there any other way to implement "overflow safe" arithmetic operations in Java other than overriding arithmetic operators?除了重写算术运算符之外,还有其他方法可以在 Java 中实现“溢出安全”算术运算吗?
【发布时间】:2014-09-08 18:13:39
【问题描述】:

所以这是我的问题。自从我学习了 Java,我就知道算术上溢和下溢可能存在,Java 编译器不会向你抱怨。现在我想出了一个 Operation 类,其中包含“溢出安全”整数 +、-、* 方法。这样如果计算溢出,就会抛出算术异常。

public final class Operation{

    public static int aDD (int a, int b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Integer Addition Overflow");
    }
    return a + b;
    }

    public static int sUB (int a, int b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Integer Subtraction Overflow");
    }
    return a - b;
    }

    public static int mUL (int a, int b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Integer Multiplication Overflow");
    }
    return a * b;
    }

    public static long aDD (long a, long b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Long Addition Overflow");
    }
    return a + b;
    }

    public static long sUB (long a, long b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Long Subtraction Overflow");
    }
    return a - b;
    }

    public static long mUL (long a, long b) {
    //do something
    if (overflow) {
    throw ArithmeticException("Long Multiplication Overflow");
    }
    return a * b;
    }
}

我想覆盖 Java 中的原始算术运算符。这样我就不需要输入int c = Operation.aDD(a, b); 来使用“溢出安全”算术运算。相反,我只需要输入int c = a + b;

但是,有人告诉我,用户定义的运算符覆盖在 Java 中是不可能的。那么,有没有办法在不覆盖 Java 中的运算符的情况下达到相同的结果。无论如何,这样做是可以接受的,包括混合 Java 和 C++ 代码。(请注意,我现在所有的代码都是用 Java 编写的。但我认为我可以使用转换器将其转换为其他语言。虽然不能保证它是错误的-免费。)我唯一的目标是允许我的旧程序使用“溢出安全”算术运算,而不必将所有a + b 重新分解为Operation.aDD(a, b)

非常感谢!

【问题讨论】:

  • 您不能覆盖 Java 中的运算符。而且你不能“混合”C/C++ 和 Java。您可以在他们之间进行通话,但会有些困难,但这比您现在拥有的要丑陋得多。
  • 您可以遵循 BigDecimal/BigInteger 的命名约定,它们有同样的问题。

标签: java overloading integer-overflow integer-arithmetic


【解决方案1】:

您可以使用Xtend。您可以将 Xtend 视为 Java 语言预处理器。它生成纯的、人类可读的 Java 代码。

关于 Xtend 中 operator overloading 的章节。以下是整数运算符“+”的重载方法定义示例:

def static operator_add(IntVar a, IntVar b) {
    return Operation.add(a, b)
}

缺点

Xtend 也是一种语言,有一些小的学习曲线。它与 Java 非常相似,但也融合了 Groovy 和 Scala 语法中的一些部分,例如方法由关键字 'def' 定义,变量由 'var' 或 'val' 定义,否则您可以编写与 Java 相同的代码。您可以选择使用 lambda 方法、扩展方法(如在 C# 中)、多行文本文字和更多好东西。 Xtend 有优秀的 Eclipse 插件,比 Scala 好很多,但不如 vanilla Java。在调试器中评估变量有一些怪癖。使用 Xtend 插件,您可以实时查看 Xtend 代码翻译成的纯 Java 代码。 Idea没有插件。与 Maven 完美集成。

替代方案

Xtend 本身基于Xtext,这是一个用于轻松创建新语言的框架。使用它和Xbase,您可以相对轻松地创建一种与Java 完全相同的新语言,除了运算符'+' 将转换为您想要的任何内容。学习曲线要​​陡峭得多,那时您必须阅读大部分 Xtext 文档。

【讨论】:

    【解决方案2】:

    正如您已经发现的那样,Java 中没有可以使用的运算符重载。

    由此可以直接看出,在纯 java 中改变表达式 int a = b + c; 的语义是不可能的。

    由于您提到已经拥有 C++ 代码,根据其结构,您可能希望从中构建一个本地库(DLL、SO)并使用 JNI 调用 C++ 代码。如果您的 C++ 代码由许多 函数/类组成,这将变得不切实际,本机调用所增加的混乱将超过代码重用的效用。不用说,您需要为每个要部署到的平台提供适当的二进制文件。

    对于纯 Java 实现,您需要将逻辑(正如您已经弄清楚的那样)重写为执行检查的显式实用方法调用。

    核心 Java 库(例如 BigInteger、BigDecimal)更进一步,将它们的类型封装到(通常是不可变的)值对象中,这些值对象(仍然)将操作作为标准方法调用进行。这使得用法更紧凑/更短,因为编译器将提供更多类型检查,并且您不需要使用类名BigDecimal sum = a.add(b); 来限定方法名。不是更漂亮,但它是一个成熟的成语。

    【讨论】:

    • 是否有语言支持java语法和运算符重载?这样,如果我将我的 Java 程序复制到它,它将编译。我提到 C++ 是因为有人告诉我 C++ 支持运算符重载。我并不是说我的代码是用 C++ 编写的,如果让您感到困惑,请见谅。
    • 此外,您提到了 BigInteger 值对象。我有个主意。我们使用MyInteger sum = a.add(b);MyLong sum = a.add(b); 怎么样?然后我发现 Integer 和 Long 类有 final 修饰符,所以我不能使用 class MyInteger extends Integerclass MyLong extends Long。我应该复制 Integer 和 Long 类并添加自己的方法来创建新类吗?还有其他方法吗?
    • @AlexVong Core java 使用不可变值对象(Integer、Long ... BigDecimal ...)。每个操作都会创建一个新对象;例如“z = x.add(y);”将结果作为新对象 z 返回。这个想法是您可以在任何地方传递值对象引用,而不必担心它们被修改。
    • 有没有办法将方法“放入” Integer 类?我想将 aDD 方法放入类中。由于 Integer 类有修饰符final,所以我不能使用继承来实现它。
    • @AlexVong 不,java.lang 类是最终的,以防止您弄乱它们的行为(请注意,java 编译器依赖于它们为自动装箱/拆箱提供精确指定的行为)。制作自己的类型,也许可以扩展 java.lang.Number。
    【解决方案3】:

    另一种选择是混合使用 Java 和 Scala。使用 Scala,您可以按您说的做(我对 Scala 没有真正的经验,但应该很容易),而且将两者混合起来很简单。

    例如见Scala: method\operator overloading

    然后,如果您编写重载算术函数库,您可以通过导入 Scala 包从 Java 中调用它们,就像在 Java 中所做的那样。

    【讨论】:

    • 很抱歉让您感到困惑。但是我的方法实际上是用java编写的,所以如果我想使用运算符重载,我需要在Scala中重写它们并导入Operation Class。
    • 也许我没有明白这一点。你愿意用 C++ 重写它们,而不是用 Scala?
    • 这是另一个混​​乱哈哈。我不会用 C++ 重写它。我会编辑它。
    • 听起来很傻。但我发现我可以使用转换器。
    • 我确实尝试了您的建议并为 NetBeans 安装了 Scala 插件。但事实证明,Scala 中的 Int 类具有 final 和 abstract 修饰符,所以我不能扩展 Int 类并覆盖该类中定义的 +、-、* 运算符。似乎开发人员设置了障碍以防止有人弄乱他们的代码。你知道如何解决它吗?
    猜你喜欢
    • 2011-07-15
    • 2019-05-13
    • 2011-05-15
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多