简介
上一遍主要讲了面相对象的基础特性,这篇文章主要讲面向对象的扩展,分别以一下几个方面来讲
- 继承,继承中的构造方法
- 抽象类
- 接口
- Mixins,操作符的覆写
继承
- 使用关键字extends继承一个类
- 子类会继承父类可见的属性和方法,不会继承构造方法
- 子类能够复写父类的方法、getter和setter
- 单继承,多态性
在Person.dart文件中:
class Person {
String name;
int age;
//私有属性
String _favorite;
//计算属性,判断是否成年
bool get isAdult => age > 18;
void work() {
print('person is working');
}
//私有方法
void _run(){
print('person is running');
}
//当继承person的子类要重写构造方法,不能继承
Person(this.name,this.age);
}
在文件中import 'Person.dart'文件
import 'Person.dart';
void main() {
var student = Student();
student.study();
student.name = '学生';
student.age = 16; //可以访问可见属性
// student._favorite = '打篮球'; //访问不了私有属性
// student._run(); //访问不了私有方法
//可以访问父类可见方法,可以重写父类的方法
//子类work方法里面调用super的work方法
/*
打印结果:
person is working
student is working
*/
student.work();
//计算属性,通过age来计算是否成人
//父类计算是大于18,子类重写大于15
print(student.isAdult); //YES
//父类的引用,子类的实现
Person person = new Student();
// person.study();//提示找不到该方法
if (person is Student) {
//父类引动study方法,实际是子类去实现该方法
person.study();
}
}
class Student extends Person {
//stu自有的方法
void study() {
print('student is studying');
}
@override //表示复写父类的标识
bool get isAudlt => age > 15;
@override
//学生可以重写父类的方法
void work() {
super.work();
print('student is working');
}
}
继承中的构造方法
- 子类的构造方法默认会调用父类的无名无参构造方法
void main() {
//默认会调用person的无名无参构造方法
var student = new Student(); //Person的构造方法
}
class Person {
String name;
//无名无参的构造方法
Person() {
print('Person的构造方法');
}
}
class Student extends Person {
int age;
}
继承中的构造方法
- 如果父类没有无名无参构造方法,则需要显示调用父类构造方法
- 在构造方法参数后使用:显示调用父类构造方法
void main() {
//Student('张三')相当于调用了Person.withName('张三')
var student = new Student('张三');
print(student.name); //张三
}
class Person {
String name;
//子类需要显示的父类构造方法,这两个其中之一
Person(this.name);
Person.withName(this.name);
}
class Student extends Person {
int age;
//Student(String name)就是显示调用super.withName(name)方法
//下面两种方式都可以,但是都写的会报错,方法不能重载
// Student(String name) : super(name);
Student(String name) : super.withName(name);
}
构造方法执行顺序
- 父类的构造方法在子类构造方法体开始执行的位置调用
- 如果有初始化列表,初始化列表会在父类构造方法之前执行
void main() {
//Student('张三')相当于调用了Person.withName('张三')
var student = new Student('张三','男');
print(student.name);
print(student.gender);
/*
打印结果:
Person构造方法先执行
Srudent构造方法后执行
张三
男
*/
}
class Person {
String name;
//子类需要显示的父类构造方法,这两个其中之一
Person(this.name);
Person.withName(this.name) {
print('Person构造方法先执行');
}
}
class Student extends Person {
int age;
final String gender ;
//子类的初始化列表在父类构造方法之前,
//父类构造方法执行在子类构造方法执行之前
Student(String name, String gender):gender = gender ,super.withName(name) {
print('Srudent构造方法后执行');
}
//初始化列表方法父类的构造方法之后编译会报错
// Student(String name, String gender):super.withName(name),gender = gender;
}
抽象类
- 抽象类使用abstract表示,不能直接被实例化
- 抽象方法不能用abstract修饰,只声明方法,没有方法实现
- 抽象类可以没有抽象方法
- 有抽象方法的类一定得声明为抽象类
void main(List<String> args) {
//抽象类不能被实例化
var person = new Person(); //报错
//抽象类只能通过子类继承来实现
var student = new Student();
student.run(); //run
}
abstract class Person {
//抽象类中抽象方法不需要用abstract,不用实现。
//子类继承去实现
void run();
}
//抽象类不能实例化,只能通过子类继承来实现实例化
class Teacher extends Person {
@override
void run() {
print('teacher run');
}
}
//抽象类不能实例化,只能通过子类继承来实现实例化
class Student extends Person {
@override
void run() {
print('Student run');
}
}
接口
- 类和接口是统一的,类就是接口
- 每个类都隐式的定义了一个包含所有实例成员的接口
- 如果是复用已有类的实现,使用继承(extends)
- 如果只是使用已有的外在行为,则使用接口(implements)
void main(List<String> args) {
var student = new Student();
student.run(); //student run
print(student.age); //15
var child = new Children();
child.run(); //children run
}
class Person {
String name;
int get age => 18;
void run() {
print('person run');
}
}
//抽象类来作为接口
abstract class AbsPerson {
void run();
}
//抽象类当成接口来调用,抽象类也是类的一种
//所以说类就是接口
//建议大家用抽象类来作为接口
class Children implements AbsPerson {
@override
void run() {
// TODO: implement run
print('children run');
}
}
// 把Person作为接口来使用,类就是接口
//Person里面的所有属性方法都要重新实现
class Student implements Person {
@override
String name;
@override
// TODO: implement age
int get age => 15;
@override
void run() {
// TODO: implement run
print('student run');
}
}
Mixins
- Mixins类似于多继承,是在多类继承中重用一个类代码的方式
- 作为Mixins类不能有显示声明构造方法
- 使用关键字with连接一个或者多个Mixins类
void main(List<String> args) {
//D多继承与A,B,C
//D类的对象可以调用A,B,C的方法
var d = new D();
//A,B,C都有a()这个方法,会执行哪个类的a()方法
//以为D extends A with B,C,按顺序执行
//C在最后面,所以执行C的a()方法
//b()方法同上原理
d.a(); //C.a
d.b();//C.b
d.c();//C.c
}
class A {
//D是继承A的,所以显示声明没有问题
A() {
}
void a() {
print('A.a');
}
}
class B {
//类B会报错,Mixins类不能显示创建构造方法
// B() {
// }
void a() {
print('B.a');
}
void b() {
print('B.b');
}
}
class C {
//类B会报错,Mixins类不能显示创建构造方法
// C() {
// }
void a() {
print('C.a');
}
void b() {
print('C.b');
}
void c() {
print('C.c');
}
}
//用 D extends A with B,C形式实现实现多继承
//先继承一个类 再在with后面跟的就是Mixins类
//在B,C类要是显示的创建构造方法,D在继承Mixins类的B,C就是有错误
class D extends A with B,C {
}
- 作为Mixins的类只能继承自Object
void main(List<String> args) {
var a = new A();
a.work(); //b.work
}
//B是Mixins类,只能继承与Object
//C并不是Object
// class B extends C {
//mixins只能继承Object
class B extends Object {
void work() {
print('B.work');
}
}
class C {
}
//B是Mixins类
class A with B {
}
下面我讲一个实例,通过用抽象类和接口来实现一个类,这样能更好理解抽象类和接口的作用
void main() {
var car = Car();
car.work(); //Using Electric
car.run(); //tyre run
var bus = Bus();
bus.work(); //Using oil
bus.run(); //tyre run
}
//发动机
abstract class Engine {
void work();
}
//燃油发动力
class OilEngine implements Engine {
@override
void work() {
print('Using oil');
}
}
//电力发动机
class ElectricEngine implements Engine {
@override
void work() {
print('Using Electric');
}
}
//轮胎
class Tyre {
String name;
void run() {
print('tyre run');
}
}
//声明类,这种是简写的方法,下面我写全
//一个汽车有轮胎并且是电发动机
class Car = Tyre with ElectricEngine;
// //下面是写全的方式
// class Car extends Tyre with ElectricEngine{
// }
class Bus = Tyre with OilEngine;
操作符覆写
- 覆写操作符需要在类中定义
返回类型 operator 操作符 (参数1,参数2,...) {
实现体
rturn 返回值
}
- 如果覆写 == ,还需要覆写对象的hashCode getter方法
- 可覆写的操作符:
<,+,|,[],>,/,^,[]=,<=,/,&,,>=,*,<<,==,-,%,>>
void main() {
var p1 = new Person(18);
var p2 = new Person(22);
//> 操作符无法比较两个对象的大小
//要覆写操作符 > 才能比较
//覆写的返回值是bool
p1 > p2;
print(p1 > p2); //false
print(p1.age); //18
//通过覆写操作符
//p1通过p1['age']取值
print(p1['age']); //18
print(p1['aa']); //0
}
class Person {
int age;
Person(this.age);
//覆写操作符 >
bool operator > (Person p) {
return this.age > p.age;
}
//覆写操作符 =
int operator [](String name) {
if(name == 'age') {
return this.age;
}
return 0;
}
}
覆写操作符你可以自己多尝试,多实践实践,慢慢就会深有体会。