继承是面向对象程序设计的三个基本特征之一,TypeScript中的类也支持继承。在定义类时可以使用extends
关键字来指定要继承的类,具体语法如下所示:
class DerivedClass extends BaseClass { }
在该语法中,我们将BaseClass叫作基类,将DerivedClass叫作派生类,派生类继承了基类。有时候,我们也将基类称作父类,将派生类称作子类。
当派生类继承了基类后,就自动继承了基类的非私有成员。
例如,下例中Circle类继承了Shape类。因此,Circle类获得了Shape类的color
和switchColor
公有成员。我们可以在Circle类的实例对象上访问color
成员变量和调用switchColor
成员函数。示例如下:
class Shape {
color: string = 'black';
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {}
const circle = new Circle();
circle.color; // 'black'
circle.switchColor();
circle.color; // 'white'
重写基类成员
在派生类中可以重写基类的成员变量和成员函数。在重写成员变量和成员函数时,需要在派生类中定义与基类中同名的成员变量和成员函数。示例如下:
class Shape {
color: string = 'black';
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {
color: string = 'red';
switchColor() {
this.color = this.color === 'red' ? 'green' : 'red';
}
}
const circle = new Circle();
circle.color; // 'red'
circle.switchColor();
circle.color; // 'green'
此例中,Circle类重写了Shape类中的color
属性和switchColor
方法。第20行,读取circle对象上的color
属性时,获取的是重写后的属性。第21行,调用circle对象上的switchColor
方法时,调用的是重写后的方法。
在派生类中,可以通过super
关键字来访问基类中的非私有成员。当派生类和基类中存在同名的非私有成员时,在派生类中只能通过super关键字来访问基类中的非私有成员,无法使用this关键字来引用基类中的非私有成员。示例如下:
class Shape {
color: string = 'black';
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {
switchColor() {
super.switchColor();
console.log(`Color is ${this.color}.`);
}
}
const circle = new Circle();
circle.switchColor();
circle.switchColor();
输出结果:
- 第11行,Circle类重写了Shape类的switchColor方法。
- 第12行,使用了super关键字来调用Shape类中的switchColor方法。
若派生类重写了基类中的受保护成员,则可以将该成员的可访问性设置为受保护的或公有的。也就是说,在派生类中只允许放宽基类成员的可访问性。例如,下例中Base类的三个成员变量都是受保护成员。在派生类Derived中不允许将其重写为私有成员。示例如下:
class Base {
protected x: string = '';
protected y: string = '';
protected z: string = '';
}
class Derived extends Base {
// 正确
public x: string = '';
// 正确
protected y: string = '';
// 错误!派生类不能够将基类的受保护成员重写为更严格的可访问性
private z: string = '';
}
由于派生类是基类的子类型,因此在重写基类的成员时需要保证子类型兼容性。示例如下:
class Shape {
color: string = 'black';
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {
// 编译错误
// 类型'(color: string) => void'不能赋值给类型'() => void'
switchColor(color: string) {}
}
派生类实例化
在派生类的构造函数中必须调用基类的构造函数,否则将不能正确地实例化派生类。在派生类的构造函数中使用“super()”
语句就能够调用基类的构造函数。示例如下:
class Shape {
color: string = 'black';
constructor() {
this.color = 'black';
}
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {
radius: number;
constructor() {
super();
this.radius = 1;
}
}
此例第18行,在Circle类的构造函数中调用了基类Shape的构造函数。这样能够保证正确地实例化基类Shape中的成员。若派生类中定义了构造函数,但没有添加“super()”
语句,那么将产生编译错误。
在派生类的构造函数中,引用了this的语句必须放在“super()”
调用的语句之后,否则将产生编译错误,因为在基类初始化之前访问类的成员可能会产生错误。示例如下:
class Shape {
color: string = 'black';
constructor() {
this.color = 'black';
}
switchColor() {
this.color =
this.color === 'black' ? 'white' : 'black';
}
}
class Circle extends Shape {
radius: number;
constructor() {
this.radius = 1;
// ~~~~
// 编译错误,必须先调用 'super' 再访问 'this'
super();
// 正确
this.radius = 1;
}
}
在实例化派生类时的初始化顺序如下:
- 初始化基类的属性。
- 调用基类的构造函数。
- 初始化派生类的属性。
- 调用派生类的构造函数。
例如,下例中的数字标识与上面的步骤序号是对应的:
class Shape {
color: string = 'black'; // 1
constructor() { // 2
console.log(this.color);
this.color = 'white';
console.log(this.color);
}
}
class Circle extends Shape {
radius: number = 1; // 3
constructor() { // 4
super();
console.log(this.radius);
this.radius = 2;
console.log(this.radius);
}
}
const circle = new Circle();
输出结果:
单继承
TypeScript中的类仅支持单继承,不支持多继承。也就是说,在extends语句中只能指定一个基类。示例如下:
class A {}
class B {}
class C extends A, B {}
// ~
// 编译错误:类只能继承一个类
接口继承类
TypeScript允许接口继承类。若接口继承了一个类,那么该接口会继承基类中所有成员的类型。例如,下例中接口B继承了类A。因此,接口B中包含了string类型的成员x
和方法类型y
。示例如下:
class A {
x: string = '';
y(): boolean {
return true;
}
}
interface B extends A {}
declare const b: B;
b.x; // 类型为string
b.y(); // 类型为boolean
在接口继承类时,接口不但会继承基类的公有成员类型,还会继承基类的受保护成员类型和私有成员类型。如果接口从基类继承了非公有成员,那么该接口只能由基类或基类的子类来实现。示例如下:
// 正确,A 可以实现接口 I,因为私有属性和受保护属性源自同一个类 A
class A implements I {
private x: string = '';
protected y: string = '';
}
// 接口 I 能够继承 A 的私有属性和受保护属性
interface I extends A {}
// 正确,B 可以实现接口 I,因为私有属性和受保护属性源自同一个类 A
class B extends A implements I {}
// 错误!C 不是 A 的子类,无法实现 A 的有属性和受保护属性
class C implements I {}
酷客网相关文章:
评论前必须登录!
注册