flutter 中最详细的继承,多态,接口讲解


前言

众所周知,dart 是一门单继承的语言,但是我们在日常开发中,会遇到各种各样的问题,比如,我们需要在dart 中实现多继承,那么改怎么办呢?本篇文章,我将和大家聊聊关于dart 中的继承,接口,混合的相关知识。

类型 解决什么问题 使用场景 限制
extends 子类继承 子类继承父类 只能继承一个父类,会继承父类的可见的属性和方法,不能继承构造函数。
Mixin(with) 实现类似多继承 不能通过多继承,获取一个类的实例 不能有构造方法,可以有实例变量
Extension(on) 使用 on 将mixin 限制为某个特定的类 在无法修改被扩展类源码的情况下使用 不能有构造方法和实例变量
Implement 声明和实现的接口,实现解耦 dart 不支持多继承,但是可以实现多接口

一、继承(Extends)

如何使用extends 关键字来继承父类

  • 子类会继承父类里面可见的属性和方法,但是不会继承构造函数
  • 子类能复写父类的getter 和 setter 方法
  • 子类重写超类的方法,要用@override
  • 子类调用超类的方法,要用super
  • 子类可以继承父类的非私有变量 (在dart 中,所有的变量和方法,默认都是公有的,我们可以使用 _ 来定义私有)

经过上面的解释,我们先来创建一个 person 的类,在类中定义人类的基本属性,私有的思想,基本的构造函数,和基本的运算

class Person {
    
    
  String? name;
  int? age;

  //  人类的思想是私有的,使用_thought 对子类不可见
  String? _thought;

  // 构造函数
  Person(this.name, this.age);

  // 计算这个人类是否成年
  bool get isAdult => (age ?? 0) >= 18;

  void run() {
    
    
    print("运行 person 类了");
  }
}

接下来我们再来定义一个学生 student 类,学生类会继承 person 类,并重写 和 调用 person 的方法

class Student extends Person {
    
    
  // 子类的构造函数,并使用super 调用了超类的方法, name 必传,age 可以为空, {int? age} 可选的意思
  Student(String name, {
    
    int? age}) : super(name, age);

  // 重写父类的方法
  
  // TODO: implement isAdult
  bool get isAdult => (age ?? 0) > 20;

  
  void run() {
    
    
    // TODO: implement run
    
    // super.run(); 如果把这里注释掉,就无法调用到超类的run() 方法了。
    super.run();

    print("运行 student 类了");
  }

  //  子类自己的方法
  void studentRun() {
    
    
    print("运行 studentRun 类了");
  }
}

接下我们使用main 方法,来验证上面的写的内容

void main() {
    
    
//   调用学生自己的方法, 传入姓名 和 年龄
  Student student = Student("tiger");

  //访问父类属性,并赋值
  student.age = 18;

  //  父类中,我们大于等于18岁是成人,但是在student 中,我们重写了父类的Adult ,设置成了20, 因此下面输出为 false
  print(student.isAdult);

  // 调用父类的方法
  student.run();
}

运行上面的代码,我们可以得到下图中的输出内容:

image-20221117111853276

接下来我们改造一下main 中的代码,一起来学习一下,继承中是如何使用多态的

void main() {
    
    
  // 继承中多态的使用,这里 age: 18 传了可选参数age
  Person person = Student("tiger", age: 18);

  // 调用超类的方法  需要注意的是,这里无法调用student 子类的方法,因为使用多态生成的对象是person, person 中没有student 的方法,如studentRun() 方法
  person.run();

  // 使用 is 的意思就是,将person 转化成 student 的对象,这样就可以访问子类的方法了。
  if (person is Student) {
    
    
    person.studentRun();
  }
}

代码运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yo1D43QX-1668667242626)(https://s2.loli.net/2022/11/17/EBmQMpI9v5dAS6u.png)]

到这里,dart 中的继承我们就讲完了,需要注意的点就是,在继承中多态的使用。

二、混合 mixins(with)

mixins 的意思就是混入的意思,就是在类中,混入其它的功能,说白了就是现有类的基础上,引入一些新的变量,下面我们一起来看一下它的特点。

  • 作为mixins 的类只能继承自object,不能继承其他的类。
  • 作为mixins 的类不能有构造函数。
  • 一个类可以mixins 多个mixin 类。
  • mixins 不是继承,也不是接口,而是一种全新的特性。

2.1、最简单的mixin

下面我们先来写一个最简单的mixin

// mixin 本身可以是抽象的,可以定义各种方法和属性,等待后续类去实现
mixin TextMixin {
    
    
//  定义属性
  var mixinValue = 2;

//  抽象方法
  void mixinTest01();

  void mixinTest02() {
    
    
    print("mixinTest02 的输出");
  }
}

class MixinModel with TextMixin{
    
    
  
  void mixinTest01() {
    
    
    // 该函数mixin 定义未实现,混入对象,必须要实现
    print("mixinTest01 需要实现此方法: ${
      
      mixinValue}");
  }

}

void main(){
    
    
  MixinModel model = MixinModel();
  model.mixinTest01();
  model.mixinTest02();

  print("mixinValue 调用的输出: ${
      
      model.mixinValue}");
}

运行上面的代码:
在这里插入图片描述

从上面的代码及输出中,我们可以得出:mixin 本身可以是抽象的,可以定义各种的方法和属性,等待后续的类去实现

2.2、on 关键字,基于某个类型的mixin

当使用on 关键字(限定类型),则表示该mixin 只能在那个类的子类中使用,这就代表了mixin 中可以调用那个类的方法和属性,请看源码。

class BaseMixin {
    
    
  void method() {
    
    
    print("method 的输出");
  }
}

mixin TextMixin1 on BaseMixin {
    
    
  void test1() {
    
    
    print("test1");
  }

  int testValue = 2;

  void test2() {
    
    
    method();
  }

  void test3();
}

class Test extends BaseMixin with TextMixin1 {
    
    
  
  void test3() {
    
    
    // TODO: implement test3

    print("需要实现的 test3");
  }

}

void main() {
    
    
  Test test = Test();
  test.test1();
  test.test2();
  test.test3();

  print(test.testValue);
}

运行上面代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMGcIr29-1668667242628)(https://s2.loli.net/2022/11/17/nhpLPqBOKix7SHu.png)]

2.3、多个mixin

前面我们学习了简单的mixin,mixin 的限定on 关键字,现在我们来看一下,多个mixin 是怎么实现的。

mixin TextMixin1 {
    
    
//  定义属性
  var mixinValue = 1;

//  抽象方法
  void mixinTest01();

  void mixinTest02() {
    
    
    print(" TextMixin1 中 mixinTest02  的输出");
  }
}

mixin TextMixin2 {
    
    
//  定义属性
  var mixinValue = 2;

  void mixinTest03() {
    
    
    print("TextMixin2 中 mixinTest03 的输出");
  }
}

class Test with TextMixin1, TextMixin2 {
    
    
  
  void mixinTest01() {
    
    
    // TODO: implement mixinTest01

    print("TextMixin1 中的抽象方法 mixinTest01 的实现");
  }
}

void main() {
    
    
  Test test = Test();
  test.mixinTest01();
  test.mixinTest02();
  test.mixinTest03();
  print(test.mixinValue);
}

运行上面的代码,输出结果如下图

在这里插入图片描述

从上面的代码及运行结果中,我们会发现,如果多个mixin 存在冲突性问题 (如:都有mixinValue 属性),后面的会覆盖前面的,没有冲突的,则都会保留,所以会存在后面的mixin 会修改掉前面的mixin 的一部分逻辑代码,不需要直接继承,就可以直接实现覆盖,避免了更复杂的多继承关系。

2.4、mixin 怎么实现多继承

dart 是单继承的语言,但是有些时候,我们也需要实现多继承的关系,既然mixin 是dart 语言中的一种新特性,那么我们该怎么使用mixin 来实现多继承的关系呢?这里将揭晓答案,请看代码

class BaseMixin {
    
    
  void init() {
    
    
    print("BaseMixin init");
  }

  BaseMixin() {
    
    
    init();
  }
}

mixin TextMixin1 on BaseMixin {
    
    
  void init() {
    
    
    print("TextMixin1 init start");
    super.init();
    print("TextMixin1 init end");
  }
}

mixin TextMixin2 on BaseMixin {
    
    
  void init() {
    
    
    print("TextMixin2 init start");
    super.init();
    print("TextMixin2 init end");
  }
}

class Test extends BaseMixin with TextMixin1, TextMixin2 {
    
    
  
  void init() {
    
    
    print("Test init start");
    super.init();
    print("Test init end");
  }
}

void main() {
    
    
  Test();
}

代码运行,执行结果如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X7sVQYH5-1668667242629)(/Users/tiger/Library/Application Support/typora-user-images/image-20221117135958044.png)]

从上面执行结果输出的log 打印顺序,是不是发现,我们已经解决了dart 中没有多继承关系的问题呢?

三、接口的实现(implement)

接口的实现,说白了就是定义一个抽象类,抽象类中仅仅定义方法,没有具体的实现,子类通过implement 的方法,在子类中进行实现具体的方法。

下面先来看一段代码

abstract class Run {
    
    
  var runValue;

  void runing() {
    
    
    print("runing");
  }
}

abstract class Eat {
    
    
  void eat();
}

class Person implements Run, Eat {
    
    
  
  var runValue = 100;

  
  void eat() {
    
    
    // TODO: implement eat
    print("Person 吃了 ${
      
      runValue} 个萝卜");
  }

  
  void runing() {
    
    
    // TODO: implement runing
    print("Person 跑了 ${
      
      runValue} 公里");
  }
}

class Tiger extends Run with Eat {
    
    
  // 抽象类中实现的方法
  // 继承抽象类可以不用实现(子类继承父类方法,可以选择是否重新)
  
  void runing() {
    
    
    // 继承抽象类,可以调用super
    super.runing();
    print("Tiger runing");
  }

  // eat 抽象类中需要实现的方法
  
  void eat() {
    
    
    // TODO: implement eat

    print("Tiger eat");
  }
}

void main() {
    
    
  Person person = Person();
  person.runing();
  person.eat();

  Tiger tiger = Tiger();
  tiger.runing();
  tiger.eat();
}

代码运行结果如下

在这里插入图片描述

从上面的代码和执行结果,我们可以看出,implement 与 extends 最大的不同就是运行后面接上多个普通或者抽象类,当我们使用 B implement A 修饰时,那么A 中的所有的属性和方法都要在B 中去实现,无论它原来是抽象方法还是普通方法。

总结

Mixin:定义了组块

Mixin on:限定了使用组块的宿主必须要继承与某个特定的类,在mixin 中可以访问到该特定类的成员和方法。

with负责组合组块,而with 后面跟的类并不一定都是mixin的,abstract class 和普通的类都可以。

extends with 修饰符会覆盖同名的方法,with 中 后一个覆盖前面的一个。

猜你喜欢

转载自blog.csdn.net/u010755471/article/details/127903629