LEC9 Extends, Casting, Higher Order Functions

1 接口和实现 Interface and Implement

早先我们介绍了类和接口,我们意识到在编写类时,有时会编写很多冗余代码。 这将我们引向继承,即某个对象不需要重新定义其父对象的所有性质。我们可以从接口和类继承,语法略有不同。 对于要继承接口性质的类,语法如下(其中SLList是一个类,而List61B是一个接口):

SLList<Blorp> implements List61B<Blorp>

类似地,一个类实现另一类性质的方法的语法如下:

Class_Name extends Class_Name

2 继承的用法

假设我们要创建一种特殊的SLList类型,称为RotatingSLListRotatingSLList应该能够执行SLList可以做的所有事情; 但是,它也应该能够向右旋转。 我们应该怎么做? 好吧,这只是继承的应用! 执行以下操作将使RotatingSLList具有SLList的所有性质以及它自己的方法rotateRight

public class RotatingSLList<Blorp> extends SLList<Blorp>{
    public void rotateRight() {...}
}

3 什么会被继承?

现在,我们在继承中有一个强大的工具; 但是,我们将定义一些规则。 现在,我们将说哪些可以继承:

  • 实例和静态变量
  • 所有方法
  • 所有嵌套类。这会随着引入私有变量而有所变化,但现在不必担心。 没有继承的一项是类的构造函数。

4 构造函数的特殊情况?

即使没有继承构造函数,我们仍然会使用它们。 我们可以使用关键字super()显式调用构造函数。 在每个构造函数的开始,已经有一个对其父类的构造函数的隐式调用。 结果是

public VengefulSLList() {
    deletedItems = new SLList<Item>();
}

等同于

public VengefulSLList() {
  super();
  deletedItems = new SLList<Item>();
}

然而,带参数的构造函数不能被隐式调用。

public VengefulSLList() {
    super(x);
    deletedItems = new SLList<Item>();
  }

不等同于:

public VengefulSLList() {
    deletedItems = new SLList<Item>();
  }

这是因为这里仅调用了空参数super()

5 is A

当一个类从另一个继承时,我们知道它必须具有所有的特质。这意味着VengefulSLListSLList,因为它具有SLList的所有特质-它也具有其他一些特质。

每个单个类都是Object类的后代,这意味着它们都是Object。

6 抽象Abstraction

正如您将在本课程的后面学到​​的那样,程序过大时可能会引起混乱。使程序更易于处理的一种方法是使用抽象。基本上,抽象隐藏了人们不需要查看的程序组件。隐藏方法的用户应该能够在不知道它们如何工作的情况下使用它们。

实现抽象动机的一种直观方法是看着自己。您是人类(除非有机器人正在研究这种情况,否则我很抱歉冒犯了您),人类可以食用食物并将其转化为能量。您无需知道如何将食物转化为能量,只需知道它的作用即可。在这种情况下,可以考虑将食物转化为能量的一种方法,输入是食物,而输出是能量。

7 强制类型转换Casting

在Java中,每个对象都有一个静态类型(在编译时定义)和一个动态类型(在运行时定义)。我们的代码可能基于这样一个事实,即某些变量可能比静态类型更具体。例如,如果我们具有以下定义:

Poodle frank  = new Poodle("Frank", 5);
Poodle frankJr = new Poodle("Frank Jr.", 15);

这个表达是合理的:

Dog largerDog = maxDog(frank, frankJr);

但是这个就不合理:

Poodle largerPoodle = maxDog(frank, frankJr);

前一条语句有效的原因是因为编译器知道一个事实,即从maxDog函数调用返回的任何东西都是Dog。 但是,在后一种情况下,即使两个Dog参数都是Poodles,编译器也不知道maxDog的返回值是否会返回Poodle

除了拥有通用的Dog之外,我们可能会有些冒险并且使用一种称为Casting的技术。 强制转换允许我们强制变量的静态类型,从根本上诱骗编译器让我们强制表达式的静态类型。 为了使largePoodle成为静态类型的Poodle,我们将使用以下代码:

Poodle largerPoodle = (Poodle) maxDog(frank, frankJr);

请注意,我们并没有改变maxDog的实际动态类型-我们只是告诉编译器maxDog会产生的是Poodle。这意味着任何对LargePoodle的引用都将具有与之关联的静态Poodle类型。

Casting虽然功能强大,但也很危险。您需要确保要casting的内容能够并且将实际发生。可以使用一些规则:

  • 您总是可以转换(到类的更通用版本),而不必担心会破坏任何东西,因为我们知道更具体的版本是通用类的版本。例如,您可以随时将贵宾犬投给狗,因为所有贵宾犬都是狗的。

  • 您还可以谨慎地强制转换(降级为某个类的特定版本),因为您需要确保在运行时不会传递任何违反您的强制转换的信息。例如,有时候狗是贵宾犬,但并非总是如此。

  • 最后,您永远都不能转换为高于或低于要转换的类的类。例如,您不能将狗投向猴子,因为猴子不在狗的直接血统之内-它是动物的孩子,因此距离较远。