第七节: Dart中抽象类abstract/接口implements/混入Mixin

Dart 语法学习目录

第一节: Dart 语法了解,认识变量,常量,数据类型
第二节: Dart 操作符(运算符)
第三节: Dart 中流程控制,条件判断以及循环语句
第四节: Dart 中常用集合 List/Map
第五节: Dart 函数使用,声明函数/匿名函数/可选参数/作用域/闭包
第六节: Dart 面向对象/构造函数/静态属性
第七节: Dart中抽象类abstract/接口implements/混入Mixin
第八节: Dart中泛型/泛型定义与使用
第九节: Dart 中的库/自定义库/内置库/第三方库


1. Dart中的抽象类和抽象方法

1.1 定义抽象类

dart中的抽象类主要用于定义标准, 子类可以继承抽象类, 也可以实现抽象类的接口

定义抽象类

  1. 抽象类通过abstract关键字类定义
  2. 抽象类不能被实例化
// 定义抽象类
abstract class Animate{

}

// 入口函数
void main(){
    //  实例化抽象类就报错
    var cat = new Animate();

}


1.2 抽象方法

抽象方法的定义

  1. Dart中没有方法体的方法我们称为抽象方法:
  2. Dart中的抽象方法不能用abstract声明,

示例

抽象类中的抽象方法

// 定义抽象方法
abstract class Animate{

    // 抽象方法
    // 抽象方法不能使用abstract声明
    // 如果使用就会报错
    void eat();
}

非抽象类中定义抽象方法就会报错,

class Dog{
  // 属性
  String name = "哈士奇";
  
  // 方法
  void getInfo(){
    print("我是${name}");
  }
  
  // 抽象方法
  void sayHello();  // 报错

}

所以不要在非抽象类中定义抽象方法


1.3 抽象类的说明
  1. 如果子类继承抽象类必须的实现里面的抽象方法
  2. 如果抽象类被当做接口实现的话, 子类必须得实现抽象类里面定义的所有属性和方法
  3. 抽象类不能被实例化,只有继承它的子类可以
  4. 抽象类通常用来定义接口,
  5. 抽象类通常具有 抽象方法
  6. 有抽象方法的类一定要声明为抽象类。


使用示例

// 定义一个抽象类
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 来实现一个或者多个接口。


接口说明:

  1. 通类和抽象类都可以作为接口,类就是接口
  2. 一个普通类要实现某个接口,覆写接口接口类(普通或抽象类)的每个成员。
  3. 如果是复用已有类的接口,使用继承(extends)。
  4. 如果只是使用已有类的外在行为,使用接口(implements)。
  5. 每个类都隐式的定义了一个包含所有实例成员的接口


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来实现多个接口


还是要注意区分

  1. extends 是继承 就算有些父类的方法不覆写,子类的实例也能使用父类的方法
  2. 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. 枚举类型

枚举类型也称为 enumerationsenums , 是一种特殊的类,用于表示数量固定的常量值。


说明:

  1. 使用 enum 关键字定义一个枚举类型.
  2. 枚举是一种有穷序列集的数据类型。
  3. 常用于代替常量,控制语句等
  4. 枚举中的每个值都有index,返回索引,索引从0开始.
  5. 枚举中的每个值可以使用values 属性列举出来

枚举的限制:

  1. 枚举不能被子类化,混合或实现。
  2. 枚举不能被显式实例化。
// 定义枚举
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 是复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。

简单说就是在类中混入其他的功能,实现类似多继承的功能


说明:

  1. 使用mixin关键字替换class定义混入类
  2. 作为Mixin的类只能继承Object,不能继承其他的类
  3. Mixin 类似于多继承,实在多类继承中重用一个类代码的方式。
  4. 作为mixins的类不能声明构造函数,不能调用 super
  5. 一个类可以混入多个Mixin
  6. 使用with 方法实现类似于多继承
  7. 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
}

你可能感兴趣的:(第七节: Dart中抽象类abstract/接口implements/混入Mixin)