继承
本章节重点介绍类继承的基本概念,包括子类的声明、单继承、方法重写、noSuchMethod()等。
声明一个子类
子类继承父类,意味着子类继承了父类的实例成员,包括实例字段和实例方法。使用extend关键字创建一个子类。
// ex511.dart
class Mammal {
String name; // 1
Mammal(this.name); // 2
void play() => print('$name is playing'); // 3
void sleep() => print('$name is sleeping'); // 3a
}
// 4
class Dolphin extends Mammal {
Dolphin(super.name); // 5
}
void main() {
var dolphin = Dolphin('Dolphin'); // 6
print(dolphin.name); // 7 Output: Dolphin
dolphin.play(); // 7a Output: Dolphin is playing
dolphin.sleep(); // 7b Output: Dolphin is sleeping
}
Mammal类有一个实例变量name;- 这是
Mammal类的构造函数; Mammal类有2个实例方法,play和sleep;Dolphin继承了Mammal类;Dolphin类的构造函数,使用super关键字引用父类;- 实例化 Dolphin 类;
Dolphin类从Mammal类继承了实例成员,包括1个实例变量和2个实例方法。
除了 Null 之外,Dart中的每个类都隐式地继承了 Object 类。此例中的 Mammal 类即:
class Mammal extends Object {
// ... (omitted for brevity)
}
但这里的 extends Object 完全没必要写出来。
单继承
Dart只允许单继承,不支持多继承,即一个类只能继承一个父类。
// ex512.dart
import 'ex511.dart';
// Error: Each class definition can have at most one extends clause.
// Try choosing one superclass and define your class to implement (or mix in) the others.
// class CuteDolphin extends Dolphin, Mammal {
// ^
class CuteDolphin extends Dolphin, Mammal {
CuteDolphin(super.name);
}
这里 CuteDolphin 试图同时继承 Dolphin 与 Mammal 类,导致编译失败。 解决方法即让 CuteDolphin 只继承 Dolphin 类。
class CuteDolphin extends Dolphin {
CuteDolphin(super.name);
}
再谈构造函数
每个类的生成式构造函数被执行时,一定会或显式或隐式地先执行父类的生成式构造函数,即构造函数的第一行代码是调用父构造函数。
// ex513.dart
import 'ex511.dart';
// Error: The implicitly called unnamed constructor from 'Mammal' has required parameters.
// Try adding an explicit super initializer with the required arguments.
// Ape();
// ^
class Ape extends Mammal {
Ape();
}
// Error: The superclass, 'Mammal', has no unnamed constructor that takes no arguments.
// class Elephant extends Mammal {}
// ^
class Elephant extends Mammal {}
Ape 类的构造函数 Ape(),隐式地调用父类的无参构造函数 Mammal() 引起编译错误,编译器提示Mammal类的unnamed构造函数需要required参数。
Elephant 类默认的构造函数Elephant(),隐式地调用父类的无参构造函数 Mammal() ,编译器提示这样的父构造函数不存在。
方法重写
子类可以重写(override)实例方法(包括运算符)、getter 和 setter。使用 @override 注解来表明重写意图。
// ex514.dart
import 'ex511.dart';
class Badger extends Mammal {
Badger(super.name);
@override
void play() => print('$name Badger is playing');
}
void main() {
var bader = Badger('B');
bader.play(); // Output: B Badger is playing
}
重写方法(A)的声明必须与被重写方法(B)在以下几个方面相匹配:
- A的返回类型必须与B的相同(或为其子类型);
- A的参数类型必须与B的相同(或为其超类型);
- 如果B接受 n 个位置参数,则A也必须接受 n 个位置参数;
- 泛型方法不能重写非泛型方法,反之亦然(泛型乃第6章内容)。
// ex515.dart
import 'ex511.dart';
class Dog extends Mammal {
Dog(super.name);
Object sing(int times) => "$name: ${'woof~ ' * times}";
}
class Corgi extends Dog {
Corgi(super.name);
@override
String sing(num times) => "$name: ${'woooof~ ' * times.toInt()}";
}
void main() {
var corgi = Corgi('Rover');
print(corgi.sing(3)); // Output: Rover: woooof~ woooof~ woooof~
}
请仔细观察此例中 Corgi.sing(int) 实例方法如何重写了 Dog.sing(int) 。
NOTE: 如果重写
==,也应当重写Object.hashCodegetter, 原因与Map的实现有关。
noSuchMethod()
先看一个例子。
// ex516.dart
class Fox {}
void main() {
dynamic f = Fox();
f.sing();
// Unhandled exception:
// NoSuchMethodError: Class 'Fox' has no instance method 'sing'.
// Receiver: Instance of 'Fox'
// Tried calling: sing()
}
此程序调用一个不存在的方法 'f.sing()', 引起运行时异常: NoSuchMethodError: Class 'Fox' has no instance method 'sing',这是Object 类中的 noSuchMethod 方法的默认行为,可以重写它进而实现一些特殊的功能。
// ex517.dart
class Gorilla {
@override
dynamic noSuchMethod(Invocation invocation) {
return """you're invoking ${invocation.memberName}
method : ${invocation.isMethod}
positionalArguments: ${invocation.positionalArguments}
namedArguments : ${invocation.namedArguments}""";
}
}
void main() {
dynamic g = Gorilla();
print(g.sing('a song'));
// Output:
// you're invoking Symbol("sing")
// method : true
// positionalArguments: [a song]
// namedArguments : {}
}