面向对象的编程-Application 11

Previously on OOP:

For each statement, methods overridden, and abstract class are the three aspects we focused in the last article, prior to the National Holiday.

Inexpr package, we have introduced a class named Expression. This is an abstract class, because it contains an abstract method called evaluate(). The reason that evaluate() is an abstract function is the statement that“too general to implement”.

Expression类(表达式)是父类,可以有很多子类,其中最为典型的子类是两个:Operandand Operator,即操作对象和操作符。比如表达式:2*3,乘号是操作符,2和3是操作数或者操作对象。

我们先从较为简单的Operand看起来,因为它里面存放的内容类似于2和3这样的数据。

存储的数据放在attribute value里面。然后Operand类的构造函数就是接收数据,并且存放到attribute value里面。

比较特殊的是Operand是Expression类和Comparable类的子类,但是前面的关键字是不同的,因为Expression类是abstract class,而Comparable类是interface。另外,一个类最多只能extends一个class,但是却可以implements multiple interfaces.

既然Operand继承了Expression类和Comparable类,那么这两个类里面的所有的abstract函数,都必须被implemented。

首先是Expression类中的abstract methodevaluate()。在函数头的前面有一个Override的annotation,为了防止函数头和Expression类中的函数头有出入,而导致overridding失败,产生编译错误。函数体是直接返回attribute value。

然后是Comparable类中的abstract methodcompareTo()。在Comparable类中,compareTo()函数接收的参数是Object类型的。有一个老问题又粉末登场了,那就是Object类型的实例不能直接和Operand类型的实例比较,所以必须要先cast。而且,不一定所有的Object都是Operand,还可以有Person,Duck,等等,也就是说,这个cast的安全性不一定有保证。

像上段代码中的cast是显然不行的。我们还是应该要先判定一下接收到的参数是不是空,如果是空指针,那么不能cast,直接返回。如果不是空指针,还要看能不能cast到Operand类,如果不可以,还是返回。只有在可以cast到Operand类的情况下,我们才能写上段代码中explicitly cast的代码。

接下来,假设是在C语言中,比较两个double数据的大小可以直接求差:

那么,在Java语言中,可不可以沿用这种方法呢?答案是显然不行。比如,this.value是5.2,other.value是5.1,那么两者的差是0.1,再强制转化为int类型是,程序会简单粗暴地判定this.value和other.value相等。

少一个“强制转化为int类型”的步骤行不行呢?答案还是显然不行。因为Comparable类的compareTo()函数的函数头中已经规定了返回值必须是int类型的。

那么,我们能采取的方法就只有一种,即用if条件语句和大于号、小于号来比较大小,也就是采取和上段代码中一样的写法。

下面,我们继续看略微复杂一些的Operator类:

这个类中存放的attributes有三个,只有一个op是操作符;另外两个都是Expression类型的,更严格地说是Operand类型的,所以本质上是double类型的。

想必宝宝们已经能完全自己看懂attributes和constructor,不需要本黄鸭再强调了,所以我们重点看一下evaluate()的函数体。如果操作符是加号,那么就两个值相加;如果操作符是乘号,那么就两个值相乘。要是操作符不是加号也不是乘号,那么就不能计算,直接返回-1。

相比之下,在实际运用中,存放一个表达式要比representing expressions as a string: e.g. "(3+2)*5"复杂很多,因为里面只包含operands and operators,比如:"3 2 + 5 *",没有括号之类的辅助。在pocket calculator中,使用的是RPN notation;而在计算机的操作系统中,tree structure更加常用。

那么,如何把看似顺序紊乱的表达式转化为数学中的表达式呢?我们必须先设定一种读取的顺序。就像在"3 2 + 5 *"中,是先提供两个操作数然后再写操作符。第一个操作数是3,第二个是2,然后两个数相加,作为新一轮运算的第一个操作数。此时,第二个操作数是5,两个数相乘。

在tree structure中,常用的遍历顺序有三种,前序、中序和后续。这三种方法全部都采用递归算法,因为树本身就是一种递归的数据结构。所谓前序遍历,就是先访问左边的节点,再访问中间,最后是右边节点。至于中序和后续,请大家课后自行学习,或者参考都灵理工大学开设的《算法和数据结构》课程。

最后,树的遍历方式又和operands and operators的存放有什么关系呢?因为树的读取有一定的顺序,存放operands and operators在树里面也要遵循一定的顺序。举一个例子,在前序遍历的树中存放"3 2 + 5 *",左边节点是3,中间节点是2,然后右边节点是加号,加号的right child是5,再右边是乘号。

欢迎使用本黄鸭编写的小程序~

微信公众号二维码:

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20181008G1DUQE00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券