【CS61B】LEC9 Extends, Casting, Higher Order Functions
LEC9 Extends, Casting, Higher Order Functions
1 接口和实现 Interface and Implement
早先我们介绍了类和接口,我们意识到在编写类时,有时会编写很多冗余代码。 这将我们引向继承,即某个对象不需要重新定义其父对象的所有性质。我们可以从接口和类继承,语法略有不同。 对于要继承接口性质的类,语法如下(其中SLList是一个类,而List61B是一个接口):
SLList<Blorp> implements List61B<Blorp>
类似地,一个类实现另一类性质的方法的语法如下:
Class_Name extends Class_Name
2 继承的用法
假设我们要创建一种特殊的SLList
类型,称为RotatingSLList
。 RotatingSLList
应该能够执行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
当一个类从另一个继承时,我们知道它必须具有所有的特质。这意味着VengefulSLList
是SLList
,因为它具有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
的内容能够并且将实际发生。可以使用一些规则:
-
您总是可以转换(到类的更通用版本),而不必担心会破坏任何东西,因为我们知道更具体的版本是通用类的版本。例如,您可以随时将贵宾犬投给狗,因为所有贵宾犬都是狗的。
-
您还可以谨慎地强制转换(降级为某个类的特定版本),因为您需要确保在运行时不会传递任何违反您的强制转换的信息。例如,有时候狗是贵宾犬,但并非总是如此。
-
最后,您永远都不能转换为高于或低于要转换的类的类。例如,您不能将狗投向猴子,因为猴子不在狗的直接血统之内-它是动物的孩子,因此距离较远。
- 原文作者:jchen
- 原文链接:http://jchenTech.github.io/post/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/CS61BLEC9/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。