目标:
5.5 编写代码,实现IS-A关系和/或HAS-A关系。
继承在Java中无处不在。可以断言,不使用继承,及时编译最微小的Java程序也几乎(几乎?) 是不可能的。为了探讨这一主题,我们将使用instanceof运算符。对于这个运算符,现在只需记住:如果被测试的引用变量是比较的类型,那么instanceof将返回true。以下代码:
将产生如下输出:
they're not equal
t1's an Object
其中的equals()方法来自何处?引用变量t1是Test类型,而Test类中没哟uequals()方法。第二if测试询问t1使用为Object类的一个实例,由于确实如此(稍后将详细介绍),因此ig测试会成功。
继续我们的话题。t1如何能够称为Object类型的一个实例,我们只是说它是说它是Test类型的吗?我们确信这里,你已经走在我们的前面,但它证明Java中的每个类都是Object类的子类(当然,Object类本身除外) 。换句话说,你曾经使用或编写过的每个类都继承自Object类。你可以使用用equals()方法、clone()方法,以及notify()、wait()等其他方法。无论何时创建一个都会自动继承Object类的所有方法。
为什么呢?比如考虑equals()方法。java的创建者正确地假定Java程序员非常普遍希望比较他们累的实例,以检验相等性。如果Object类没有equals()方法,则你以及所有的其他Java程序员都将不得不机子编写这样一个方法。一个equals()方法被继承了数十亿次。
对于考试,你只需要知道可以通过扩展类在Java中创建继承关系。理解继承的使用还有两个重要的理由:
- 促进代码服用。
- 使用多态性。
让我们从服用开始。一种常见的设计方法是:创建类的相当一般化的一个版本,其目标是创建继承它的更为特殊化的子类。例如:
输出如下:
displaying shape
moving game piece
继承的第二种(以及相关的) 用法是允许以多态方式访问你的类,多态性也是由接口提供的一种能力,但是将在稍后再介绍它。假定你有一个GameLauncher类,它想虚幻遍历一列不同类型的GameShape对象,并且在每个对象上调用display()方法。当你编写这个类时,你并不知道其他任何人将编写的每种可能的GameShape子类,但你确信自己不希望重新编写代码,仅仅是由于有人决定在6个月后腰构建一个Dice形状。
关于多态性(“许多形式”) 的美妙之处是:可以将GameShape的任何子类视作GameShape。换句话说,可以在你的GameLauncher类中编写代码,指出:”只要是继承自(扩展)“GameShape,那么并不在意对象的类型是什么。
摄像有两个特殊子类PlayerPiece和TilePiece,它们扩展了更加泛型的GameShape类:
现在设想测试类具有一个方法,它带有声明的GameShape变元类型,这意味着它可以获取任何类型的GameShape。换句话说,GameShape的任何子类都可以传递给具有GameShape类型的变元的方法。以下代码:
输出如下:
displaying shape
displaying shape
关键点在于,doShape()方法是用GameShape变元声明的,但可以向它传递GameShape的任何子类型(这里就是一个子类) 。然后,该方法可以调用GameShape的任何方法,而无需考虑传递给方法的对象实际运行时的类类型,尽管有这方面的暗示。doShapes()方法只知道对象是GameShape类型,因为这是参数的声明方式。使用声明为GameShape类型的引用变量——而不考虑该变量是方法参数、局部变量——意味着只能对它调用GameShape的方法。
能够在引用上调用的方法完全依赖于变量的声明类型,而不管该引用的实际对象是什么。这意味着本鞥使用GameShape变量调用(比如说) getAdjacent()方法,即使传入的对象是TilePiece类型也是如此(当探讨接口时,将再次看到一点)。
IS-A关系和HAS-A关系
对于考试,要能够看懂代码,并判断是IS-A还是HAS-A关系。规则很简单,因此,这应该是几个几乎不需要动脑筋就能够正确回答的问题之一。