Dart中抽象类abstract、接口implements、混入Mixin

5,184 阅读5分钟

一、Dart中的抽象类和抽象方法

1.1 定义抽象类

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

// 定义抽象类  通过abstract关键字类定义
abstract class A{

}

// 抽象类不能被实例化
void main(){
    //  实例化抽象类就报错
    var cat = new A();

}

1.2 定义抽象方法

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

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

// 不能在非抽象类中定义抽象方法
class Man{
  // 属性
  String name = "小美";
  
  // 方法
  void getInfo(){
    print("我是${name}");
  }
  
  // 抽象方法
  void sayHello();  // 报错

}

1.3 抽象类也可以有非抽象方法

抽象类里也可以定义非抽象方法, 这些方法都可以被子类多调用

// 定义一个抽象类
abstract class Person{

    // 抽象方法
    eat();

    // 非抽象方法
    printInfo(){
        print("我就是抽象类里的一个非抽象方法");
    }
}

// 子类继承抽象类
class Man extends Person{
    String name;
    Man(this.name){}

    // 子类必须实现抽象类的方法
    @override
    eat() {
        print("${this.name}喜欢吃");
    }
}

// 子类继承抽象类
class Child extends Person{
    String name;
    Child(this.name){}

    // 子类必须实现抽象类的方法
    @override
    eat() {
        print("${this.name}喜欢写代码");
    }
}

void main(){
    Man man = new Man("小美");
    Man.eat();  // 小美喜欢吃

    // 子类调用继承的抽象类中的非抽象方法
    man.printInfo(); 
    
    Child child = new Child("嘟嘟");
    child.eat();  // 嘟嘟喜欢吃
    child.printInfo();  // 我就是抽象类里的一个非抽象方法
}

1.4 抽象类的说明

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();  // 波斯猫喜欢吃鱼
}

二、多态

多态就是父类定义一个方法不去实现,让继承他的子类去实现, 每个子类有不同的表现(父类是抽象类,并且有抽象方法)

// 定义一个抽象类
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.1 implements

一个类通过使用关键字implements 来实现一个或者多个接口。

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

// 通过class关键字定义普通类 
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();   // 他叫做小明
}

// implements关键字实现接口
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.2抽象类接口

将抽象类作为接口使用(类就是接口),让子类来实现(关键字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方法
}


3.3 多个接口

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岁了

} 

四、枚举

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

说明: 1.使用 enum 关键字定义一个枚举类型.
2.枚举是一种有穷序列集的数据类型。
3.常用于代替常量,控制语句等
4.枚举中的每个值都有index,返回索引,索引从0开始.
5.枚举中的每个值可以使用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;
    }
} 

五、混入

Mixin 是复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。简单说就是在类中混入其他的功能,实现类似多继承的功能。

说明:
1.使用mixin关键字替换class定义混入类
2.作为Mixin的类只能继承Object,不能继承其他的类
3.Mixin类似于多继承,实在多类继承中重用一个类代码的方式。 4.作为mixins的类不能声明构造函数,不能调用 super 5.一个类可以混入多个Mixin类
6.使用with 方法实现类似于多继承
7.mixins绝不是继承也不是接口, 而是一种全新的特性

5.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
} 

5.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

} 

注意混入的类不能有构造函数 无论是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{
}

5.3 混入类不能继承其他类

// 被继承的类
class Person{
    int age ;
    Person(this.age);

    printInfo(){
        print("被继承的类Person, 年纪${this.age}");
    }
}

// B 是混入类,不能继承其他类, 报错
class B extends Person{
    printB(){
        print("混入类B");
    }
}


// 混入
class Student with B{

}

5.4 混入不影响继承

// 第一个类
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);  // BB
    student.printA(); // 这是普通类A
    student.printB(); // 这是普通类B
}