【问题标题】:Java operator overloadingJava 运算符重载
【发布时间】:2009-10-16 11:56:04
【问题描述】:

不使用运算符会使我的代码晦涩难懂。

(aNumber / aNother) * count

优于

aNumber.divideBy(aNother).times(count)

在 6 个月没有写一条评论后,我不得不对上面的简单操作写评论。通常我会重构,直到我不需要评论。这让我意识到,数学符号和数字比书面形式更容易阅读和理解。

例如

TWENTY_THOUSAND_THIRTEEN.plus(FORTY_TWO.times(TWO_HUNDERED_SIXTY_ONE))

更晦涩
20013 + 42*261

那么你知道在Java中使用运算符重载的情况下摆脱晦涩难懂的方法吗?

更新:我认为我对 cme​​ts 的夸大不会给我带来这样的麻烦。我承认我需要在 6 个月内写几次评论。但总共不超过10行。很抱歉。

更新 2: 另一个例子:

budget.plus(bonusCoefficient.times(points))

更晦涩
budget + bonusCoefficient * points

我不得不停下来思考第一个,第一眼看起来像是乱七八糟的词,另一方面,我第一眼就明白了第二个的意思,它非常清晰整洁。我知道这在 Java 中无法实现,但我想听听一些关于我的替代方案的想法。

【问题讨论】:

  • 这不是关于运算符重载的问题(java 并不真正支持)。更多关于运算符与方法链接的问题...
  • “在 6 个月内没有写过一条评论”表明您遇到的问题比缺少运算符重载要大得多。
  • cmets 通常是不安全或糟糕的程序员的标志。 cmets 也会很快过时;尝试进行一或五次重构,然后告诉我这些 cmets 有多准确。但是,代码始终是最新的。
  • @geowa4(和@nimcap):我可以向你保证,我的 cmets 不是因为我不安全或在任何方面都不好,而是主要是因为在现实世界中你不能总是信任任何其他使用代码的人对事物的有效输入和预期输出同样聪明或同样了解。重构直到代码不需要 cmets 是主观的,因为不是每个人都会分享您对需求的看法。归根结底,写 cmets(在需要的地方)不是我为自己做的事情,更多的是对您的同行和追随者的礼貌,让他们的生活更轻松。
  • @geowa4 我同意那个博客,过多的评论是不好的。然而,没有评论更糟糕。此外,如果您使用继承,OO 编码几乎需要它。尝试在不阅读 javadocs 的情况下正确扩展核心 java 类。我保证你会有一个错误。

标签: java coding-style operators


【解决方案1】:

不久前我对使用 BigInteger 有类似的要求。这取决于你想要做什么样的性能牺牲:

对于测试代码(无性能要求)我创建了一个解析器:

Parser.execute("20031 + 42*261");

对于生产代码,我尝试使用聪明的构建器:

ExpressionBuilder e = new ExpressionBuilder();
e.add(20031,e.mul(42,261)).solve();

但到最后,人们往往会习惯于愚蠢的代码,甚至更喜欢它:在 jave 中没有运算符重载,所以要忍受它,不要尝试创建“聪明”的抽象。

BigInteger a = new BigInteger(20031);
BigInteger b = new BigInteger(42);
BigInteger a = new BigInteger(261);

BigInteger res = a.plus(b.times(c));

无论如何,请检查适合您团队的方法。

编辑: 我没有具体评论你的例子。但显然,无论您采用哪种方法,调用 42,FORTY_TWO 都不会使代码非常清晰。为常量选择正确的名称对澄清代码大有帮助:

BigInteger dailyOperatingCost = OFFICE_RENTAL.plus( CONTRACTOR_NUMBER.times(CONTRACTOR_RATE);

【讨论】:

    【解决方案2】:

    我想我们都可以很高兴,运算符重载在java中是支持的。

    Frame myFrame = new Frame() + myButton + new JList() / new Separator() - 50;
    

    谁愿意维护这样的东西?

    【讨论】:

    • 是的,我知道运算符重载可能会被滥用,但有时这正是您需要的。尽管如此,任何功能都可能被滥用。
    • 这是一个谬误的论点......这就像说你很高兴没有驾照,因为开车会杀了你。任何强大的语言功能都可能被滥用...
    • 真正的好处是别人没有驾照可以杀了我。 Java 的一个优点是它缺少许多危险的特性。
    • @starblue: One advantages of Java is that it lacks many dangerous features 我同意。我侄子的自行车两侧有小轮子,以免摔倒。他也喜欢它,胜过那些成年人无所畏惧地使用的危险自行车。更严重的是,运算符重载不是 C++ 中的主要(甚至次要)错误来源。事实上,尽管 Gosling 认为,C++ 中错误的主要来源是与 C 相关的(C 数组溢出、malloc 分配的泄漏等)。运算符重载使代码更清晰,仅此而已,并且与任何其他函数一样危险。
    【解决方案3】:

    Java 编译器可以使用java-oo plugin

    【讨论】:

      【解决方案4】:

      在 Java 中,请克服它,并将方法名称用作 BigInteger 作为标准,以便您习惯它们。

      或者看看 Scala。

      至少我是这样做的。

      【讨论】:

        【解决方案5】:

        我喜欢 vdr 关于语言的想法。但是您可以跳过解析并构建一个非常简单的评估。这是一个基于堆栈的评估。在 15 分钟内完成组装,远非完美,但你明白了。

        import java.util.*;
        import java.math.BigInteger;
        
        public class BigEval {
        
          private static abstract class Op{
            abstract void apply(LinkedList<BigInteger> stack);
          }
        
          private static Map<Character, Op> charToOp = new HashMap<Character, Op>();
        
          static {
            Op plus = new Op() {
              void apply(LinkedList<BigInteger> stack) { stack.push(stack.pop().add(stack.pop())); }
            };
            Op mult = new Op() {
              void apply(LinkedList<BigInteger> stack) { stack.push(stack.pop().multiply(stack.pop())); }
            };
            Op dup = new Op() {
              void apply(LinkedList<BigInteger> stack) { stack.push(stack.peek()); }
            };
        
            charToOp.put('+', plus);
            charToOp.put('*', mult);
            charToOp.put('d', dup);
          }
        
        
          public static BigInteger eval(Object ... expression){
            return eval(new LinkedList(Arrays.asList(expression)), new LinkedList());
          }
        
          private static BigInteger eval(LinkedList expression, LinkedList<BigInteger> stack){
            while (expression.size()>0){
              Object next = expression.pop();
              if (next instanceof BigInteger){
                stack.push((BigInteger)next);
              } else if (next instanceof Number) {
                stack.push(BigInteger.valueOf(((Number)next).longValue()));
              } else {
                charToOp.get(next).apply(stack);
              }
            }
            return stack.pop();
          }
        
          public static void main(String[] args) {
            System.out.println(
              BigEval.eval(3, 4, '+', 5, '*')
            );
          }
        
        } // end of class
        

        【讨论】:

        • 我喜欢这个主意。您的解决方案让我想起了很久以前我的 CS 课程中的一些简单、优雅的解决方案。今天很容易被“XML-this”和“web-that”分心。我认为您的解决方案最终会更具可读性和可维护性,因为它是针对此问题规模的通用解决方案。
        【解决方案6】:

        您不能在 Java 中重载运算符,也没有办法解决。

        您可以使用类似 Java 的替代 JVM 语言。 Groovy 可能比其他 JVM 语言更接近 Java 本身。见Operator overloading in Groovy

        【讨论】:

        • Scala 可以用与 Java 几乎相同的方式编写,同时允许您扩展到函数式编程。此外,James Strachan(Groovy 的创建者)表示,如果有人在 2003 年向他展示“Scala 编程”,他将永远不会创建 Groovy macstrac.blogspot.com/2009/04/…>。
        【解决方案7】:

        在 Java 中没有办法重载运算符。你为什么不切换到C#?! =)

        或者尝试用 Java 编写更短的命名方法:

        aNumber.div(aNother).mul(count)
        

        【讨论】:

        • 在大多数应用程序或大多数工作场所中切换到 C# 是不可行的。尤其是当有其他 jvm 语言可以与您现有的代码集成时。
        【解决方案8】:

        如果您只是处理数值,则在表达式中使用标准包装类(Integer、Double 等)时将自动拆箱为其原始类型。以下是完全有效的:

          Integer five = new Integer(5);
        
          Integer ten = new Integer(10);
        
          System.out.println(five * ten);
        
          System.out.println(ten / five);
        

        【讨论】:

        • 不幸的是,我正在处理我写的其他类:)
        【解决方案9】:

        最简单的方法可能是将子表达式分配给具有有意义名称的变量。

           discriminant = b.times(b).plus(four.times(a).times(c));
        

        您甚至可能还想计算 bsquared 和 ac4(fourac?)。

        这作为有意义的 cmets 起作用,而且它可以缩短源代码行的长度。

        【讨论】:

          【解决方案10】:

          我和你一样,我现在发现类名是一个方便的文档,以便编写自文档代码。您将不得不自己解决注释标记所带来的困难,因为这是 Java 中的既定做法。至于你的意图在哪里寻找,最终的结果是一片云。现在,如果我接近必须保护的信息,我绝对不想要的就是让客户的数据通过小而紧凑的代码传递到云中。我扩展了一切,从 C 中的后处理器开始工作,并在本地调用 Java 中我真正打算保护的任何东西。

          我在你原来的帖子中看到的,虽然不是偏袒,但可以这样调用相当大量的代码工作:

          doIt();
          

          这该死的可悲的是,不仅要求糖果代码并执行它,而且除非您同时编写编译器和运行上述代码的安全域,否则在运行关键代码的大型并行系统中肯定会一事无成.我不是,也不打算成为,甚至有点滑稽——信任是你在下午 6 点的新闻中读到的东西,并且受到那些想把你的午餐从你身边抢走的竞争对手的喜爱。有几十种语言可以满足你的要求,你为什么在 Java 讨论中问它?

          也许是时候介绍一种可以做到这一点的语言了 == 不乏这样做的 api 及其支持者。代码应该是自记录的,对于 Java 来说,类名就是它的名字。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-15
            • 2013-02-14
            • 2018-05-13
            • 2011-11-14
            • 1970-01-01
            • 1970-01-01
            • 2012-02-11
            • 2012-10-15
            相关资源
            最近更新 更多