第七节: Dart中抽象类abstract/接口implemen
Dart 语法学习目录
第一节: Dart 语法了解,认识变量,常量,数据类型
第二节: Dart 操作符(运算符)
第三节: Dart 中流程控制,条件判断以及循环语句
第四节: Dart 中常用集合 List/Map
第五节: Dart 函数使用,声明函数/匿名函数/可选参数/作用域/闭包
第六节: Dart 面向对象/构造函数/静态属性
第七节: Dart中抽象类abstract/接口implements/混入Mixin
第八节: Dart中泛型/泛型定义与使用
第九节: Dart 中的库/自定义库/内置库/第三方库
1. Dart中的抽象类和抽象方法
1.1 定义抽象类
dart中的抽象类主要用于定义标准, 子类可以继承抽象类, 也可以实现抽象类的接口
定义抽象类
- 抽象类通过abstract关键字类定义
- 抽象类不能被实例化
// 定义抽象类
abstract class Animate{
}
// 入口函数
void main(){
// 实例化抽象类就报错
var cat = new Animate();
}
1.2 抽象方法
抽象方法的定义
- Dart中没有方法体的方法我们称为抽象方法:
- Dart中的抽象方法不能用abstract声明,
示例
抽象类中的抽象方法
// 定义抽象方法
abstract class Animate{
// 抽象方法
// 抽象方法不能使用abstract声明
// 如果使用就会报错
void eat();
}
非抽象类中定义抽象方法就会报错,
class Dog{
// 属性
String name = "哈士奇";
// 方法
void getInfo(){
print("我是${name}");
}
// 抽象方法
void sayHello(); // 报错
}
所以不要在非抽象类中定义抽象方法
1.3 抽象类的说明
- 如果子类继承抽象类必须的实现里面的抽象方法
- 如果抽象类被当做接口实现的话, 子类必须得实现抽象类里面定义的所有属性和方法
- 抽象类不能被实例化,只有继承它的子类可以
- 抽象类通常用来定义接口,
- 抽象类通常具有 抽象方法
- 有抽象方法的类一定要声明为抽象类。
使用示例
// 定义一个抽象类
abstract class Animal{
// 抽象方法
eat();
}
// 子类继承抽象类
class Dog extends Animal{
String name;
Dog(this.name){}
// 子类必须实现抽象类的方法
@override
eat() {
print("${this.name}喜欢吃骨头");
}
}
// 子类继承抽象类
class Cat extends Animal{
String name;
Cat(this.name){}
// 子类必须实现抽象类的方法
@override
eat() {
print("${this.name}喜欢吃千层饼");
}
}
void main(){
Dog dog = new Dog("哈士奇");
dog.eat(); // 哈士奇喜欢吃骨头
Cat cat = new Cat("加菲猫");
cat.eat(); // 加菲猫喜欢吃千层饼
}
1.4 抽象类也可以有非抽象方法
抽象类里也可以定义非抽象方法, 这些方法都可以被子类多调用
// 定义一个抽象类
abstract class Animal{
// 抽象方法
eat();
// 非抽象方法
printInfo(){
print("我就是抽象类里的一个非抽象方法");
}
}
// 子类继承抽象类
class Dog extends Animal{
String name;
Dog(this.name){}
// 子类必须实现抽象类的方法
@override
eat() {
print("${this.name}喜欢吃骨头");
}
}
// 子类继承抽象类
class Cat extends Animal{
String name;
Cat(this.name){}
// 子类必须实现抽象类的方法
@override
eat() {
print("${this.name}喜欢吃千层饼");
}
}
void main(){
Dog dog = new Dog("哈士奇");
dog.eat(); // 哈士奇喜欢吃骨头
// 子类调用继承的抽象类中的非抽象方法
dog.printInfo(); // 我就是抽象类里的一个非抽象方法
Cat cat = new Cat("加菲猫");
cat.eat(); // 加菲猫喜欢吃千层饼
dog.printInfo(); // 我就是抽象类里的一个非抽象方法
}
2. 多态
2.1 正常使用的多态
多态就是父类定义一个方法不去实现,让继承他的子类去实现, 每个子类有不同的表现
其实刚刚讲的抽象类的示例就是一个多态, 父类定义eat方法, 但是没有实现他, 不同的子类实现了eat方法的不同功能,这就是多态
2.2 子类的实例赋值给父类的引用
Dart 中的多态 讲子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果
// 定义一个抽象类
abstract class Animal{
// 抽象方法
eat();
}
// 子类继承抽象类
class Dog extends Animal{
String name;
Dog(this.name){}
// 子类必须实现抽象类的方法
@override
eat() {
print("${this.name}喜欢吃骨头");
}
// 子类自己的方法
run(){
print("${this.name}在愉快的奔跑");
}
}
void main(){
// 通过子类 定义实例
// Dog dog = new Dog("哈士奇");
// dog.eat(); // 哈士奇喜欢吃骨头
// dog.run(); // 哈士奇在愉快的奔跑
// 将子类的实例赋值给了父类的引用
Animal dog = new Dog("哈士奇");
dog.eat(); // 哈士奇喜欢吃骨头
// dog.run(); // 这个时候就报错
}
我们会发现如果将子类的实例赋值给了父类的引用, 那么将只能使用父类限定的方法, 子类自己实现的自定义的方法将不能调用
3. Dart中的接口
3.1 dart 接口了解
官网的定义
一个类通过使用关键字implements
来实现一个或者多个接口。
接口说明:
- 通类和抽象类都可以作为接口,类就是接口
- 一个普通类要实现某个接口,覆写接口接口类(普通或抽象类)的每个成员。
- 如果是复用已有类的接口,使用继承(extends)。
- 如果只是使用已有类的外在行为,使用接口(implements)。
- 每个类都隐式的定义了一个包含所有实例成员的接口
3.2 普通类接口
3.2.1 定义普通类
通过class关键字定义普通类
class Person{
// 属性
String name;
// 构造函数
Person(this.name);
// 方法
void sayHello(){
print("大家好,我叫${name}");
}
}
void main(){
Person xm = new Person("小明");
xm.sayHello(); //大家好,我叫小明
}
3.2.2 extends关键字实现类的继承
定义子类继承这个父类
// 父类
class Person{
// 属性
String name;
// 构造函数
Person(this.name);
// 方法
void sayHello(){
print("大家好,我叫${name}");
}
}
// 子类通过extends 继承父类的方法
class Student extends Person{
String name;
Student(this.name):super(name);
// 子类自己的方法
void getInfo(){
print("他叫做${name}");
}
}
void main(){
Student xm = new Student("小明");
xm.sayHello(); //大家好,我叫小明
xm.getInfo(); // 他叫做小明
}
3.2.3 implements关键字实现接口
现在将父类作为接口,让子类来实现这个接口
// 接口类
class Person{
// 属性
String name;
// 构造函数
Person(this.name);
// 方法
void sayHello(){
print("大家好,我叫${name}");
}
}
// 实现接口类
class Student implements Person{
// 覆写接口的属性
@override
String name;
Student(this.name);
// 覆写接口的方法
@override
void sayHello(){
print("大家好,我叫${name}");
}
// 子类自己的方法
void getInfo(){
print("他叫做${name}");
}
}
void main(){
Student xm = new Student("小刚");
xm.sayHello(); //大家好,我叫小刚
xm.getInfo(); // 他叫做小刚
}
这个时候我们就会发现, 这是不是子类继承父类的关系,而是父类就是定义接口,实现接口的子类要覆写接口中的所有属性和方法.
3.3 抽象类接口
建议:
将抽象类作为接口使用(类就是接口),让子类来实现(关键字implements
来实现)
因为抽象类的所有的方法不用定义方法体, 而普通类定义的方法需要实现方法体, 反正这些方法在作为接口的时候都是需要被实现接口的子类覆写的,那么有没有方法体已经不重要了,所以推荐使用抽象类作为接口
// 定义一个抽象类接口
abstract class Db{
String uri;
// 抽象方法
add(String data);
save();
delete();
}
// Mysql 实现接口
class Mysql extends Db{
@override
String uri;
Mysql(this.uri);
// 子类必须实现抽象类的方法
@override
add(data){
print("这是Mysql的${data}");
}
@override
save(){
print("这是Mysql的save方法");
}
@override
delete(){
print("这是Mysql的delete方法");
}
}
void main(){
Mysql mysql = new Mysql("mysql");
mysql.add("123456"); // 这是Mysql的123456
mysql.save(); // 这是Mysql的save方法
mysql.delete(); // 这是Mysql的delete方法
}
4. Dart中一个类实现多个接口
extends 关键字的继承没发做到多继承,如果需要继承多个类, extends是不能实现的,
那么我们就可以把需要继承的类作为接口,使用关键字implements来实现多个接口
还是要注意区分
-
extends
是继承 就算有些父类的方法不覆写,子类的实例也能使用父类的方法 -
implements
是实现接口,不管实现多少父类接口, 父类里的所有方法都需要覆写的,
实现多接口的示例:
// 抽象类A接口
abstract class A{
String name;
// 抽象方法
printA();
}
// 抽象类B接口
abstract class B{
int age;
// 抽象方法
printB();
}
// Student 类实现 A和B 两个接口
class Student implements A,B{
// 覆写接口的属性
@override
String name;
int age;
Student(this.name,this.age);
// 实现A接口的方法
@override
void printA(){
print("实现A接口的方法");
}
// 实现B接口的方法
@override
void printB(){
print("实现B接口的方法");
}
// 子类自己的方法
void sayHello(){
print("大家好, 我叫${name},今年${age}岁了");
}
}
// 入口函数
void main(){
// 实例化子类
Student xm = new Student("小刚",8);
xm.printA(); // 实现A接口的方法
xm.printB(); // 实现B接口的方法
xm.sayHello(); //大家好, 我叫小刚,今年8岁了
}
5. 枚举类型
枚举类型也称为 enumerations
或 enums
, 是一种特殊的类,用于表示数量固定的常量值。
说明:
- 使用
enum
关键字定义一个枚举类型. - 枚举是一种有穷序列集的数据类型。
- 常用于代替常量,控制语句等
- 枚举中的每个值都有
index
,返回索引,索引从0开始. - 枚举中的每个值可以使用
values
属性列举出来
枚举的限制:
- 枚举不能被子类化,混合或实现。
- 枚举不能被显式实例化。
// 定义枚举
enum Color {
red,
green,
blue,
// 不能指定值,会报错的
// whie = "#fff"
}
// 入口函数
void main(){
// index 获取索引
print(Color.red.index);
print(Color.green.index);
print(Color.blue.index);
// value
print(Color.red);
// 枚举值
List list = Color.values;
print(list); //[Color.red, Color.green, Color.blue]
// 流程控制
var color = Color.red;
switch(color){
case Color.red:
print("红色");
break;
case Color.green:
print("绿色");
break;
case Color.blue:
print("蓝色");
break;
}
}
6. Mixin
混入
官方定义:
Mixin
是复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。
简单说就是在类中混入其他的功能,实现类似多继承的功能
说明:
- 使用
mixin
关键字替换class
定义混入类 - 作为
Mixin
的类只能继承Object,不能继承其他的类 -
Mixin
类似于多继承,实在多类继承中重用一个类代码的方式。 - 作为
mixins
的类不能声明构造函数,不能调用 super - 一个类可以混入多个
Mixin
类 - 使用
with
方法实现类似于多继承 -
mixins
绝不是继承 也不是接口, 而是一种全新的特性
6.1 混入普通的类
使用关键字with
混入普通的类
// 定义普通混入类
class A{
String name = "AA";
printA(){
print("这是普通类A");
}
}
class B{
String name = "BB";
printB(){
print("这是普通类B");
}
}
// 混入两个类A,B
class Student with A,B{
}
// 入口函数
void main(){
// 实例化子类
Student student = new Student();
print(student.name); // BB
student.printA(); // 这是普通类A
student.printB(); // 这是普通类B
}
在混入多个类是,类中有相同 的属性或方法,后混入的会将先混入的覆盖掉
6.2 使用mixin
定义混入类
// 使用mixin关键字定义混入类
mixin A{
String name = "AA";
printA(){
print("这是普通类A");
}
}
mixin B{
String name = "BB";
printB(){
print("这是普通类B");
}
}
class Student with A,B{
}
// 入口函数
void main(){
// 实例化子类
Student student = new Student();
print(student.name); // BB
student.printA(); // 这是普通类A
student.printB(); // 这是普通类B
}
6.3 注意混入的类不能有构造函数
无论是class
定义的混入类,还是mixin
定义的混入类, 有构造函数就不错
但是如果class
定义的类不作为混入类使用,就不会报错
// 混入类不能有构造函数会报错
// mixin 定义的混入类
mixin A{
String name = "AA";
// A(this.name); // 报错
printA(){
print("这是普通类A");
}
}
// class定义的混入类
class B{
String name = "BB";
// B(this.name); // 报错
printB(){
print("这是普通类B");
}
}
// 混入类
class Student with A,B{
}
6.4 混入类不能继承其他类
// 被继承的类
class Person{
int age ;
Person(this.age);
printInfo(){
print("被继承的类Person, 年纪${this.age}");
}
}
// B 是混入类,不能继承其他类, 报错
class B extends Person{
printB(){
print("混入类B");
}
}
// 混入
class Student with B{
}
6.5 混入不影响继承
// 第一个类
mixin A{
String name = "AA";
printA(){
print("这是普通类A");
}
}
class B{
String name = "BB";
printB(){
print("这是普通类B");
}
}
// 被继承的类
class Person{
int age ;
Person(this.age);
printInfo(){
print("被继承的类Person, 年纪${this.age}");
}
}
// 继承一个类混入两个类
class Student extends Person with A,B{
Student(int age):super(age);
}
void main(){
// 实例化类
Student student = new Student(18);
// 继承类
student.printInfo(); // 被继承的类Person, 年纪18
// 混入类的方法
print(student.name); // AA
student.printA(); // 这是普通类A
student.printB(); // 这是普通类B
}