flutter 中最详细的继承,多态,接口讲解
众所周知,dart 是一门单继承的语言,但是我们在日常开发中,会遇到各种各样的问题,比如,我们需要在dart 中实现多继承,那么改怎么办呢?本篇文章,我将和大家聊聊关于dart 中的继承,接口,混合的相关知识。
| 类型 | 解决什么问题 | 使用场景 | 限制 |
|---|---|---|---|
| extends | 子类继承 | 子类继承父类 | 只能继承一个父类,会继承父类的可见的属性和方法,不能继承构造函数。 |
| Mixin(with) | 实现类似多继承 | 不能通过多继承,获取一个类的实例 | 不能有构造方法,可以有实例变量 |
| Extension(on) | 使用 on 将mixin 限制为某个特定的类 | 在无法修改被扩展类源码的情况下使用 | 不能有构造方法和实例变量 |
| Implement | 声明和实现的接口,实现解耦 | dart 不支持多继承,但是可以实现多接口 | 无 |
如何使用extends 关键字来继承父类
经过上面的解释,我们先来创建一个 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();
}
运行上面的代码,我们可以得到下图中的输出内容:

接下来我们改造一下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 的意思就是混入的意思,就是在类中,混入其它的功能,说白了就是现有类的基础上,引入一些新的变量,下面我们一起来看一下它的特点。
下面我们先来写一个最简单的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 本身可以是抽象的,可以定义各种的方法和属性,等待后续的类去实现
当使用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);
}
运行上面代码

前面我们学习了简单的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 的一部分逻辑代码,不需要直接继承,就可以直接实现覆盖,避免了更复杂的多继承关系。
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();
}
代码运行,执行结果如下图

从上面执行结果输出的log 打印顺序,是不是发现,我们已经解决了dart 中没有多继承关系的问题呢?
接口的实现,说白了就是定义一个抽象类,抽象类中仅仅定义方法,没有具体的实现,子类通过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 中 后一个覆盖前面的一个。
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主
我使用的是遗留数据库,所以我无法控制数据模型。他们使用了很多多态链接/连接表,就像这样createtableperson(per_ident,name,...)createtableperson_links(per_ident,obj_name,obj_r_ident)createtablereport(rep_ident,name,...)其中obj_name是表名,obj_r_ident是标识符。因此链接的报告将按如下方式插入:insertintoperson(1,...)insertintoreport(1,...)insertintoreport(2,...)insertint
所以我只是对此感到好奇:DataMapper为其模型使用混合classPostincludeDataMapper::Resource虽然active-record使用继承classPost有谁知道为什么DataMapper选择这样做(或者为什么AR选择不这样做)? 最佳答案 它允许您从另一个不是DM类的类继承。它还允许动态地将DM功能添加到类中。这是我正在处理的模块中的类方法:defdatamapper_classklass=self.dupklass.send(:include,DataMapper::Resource)klass
我正在使用带有单个“帐户”表的STI模型来保存用户和技术人员的信息(即用户...8)错误:test_the_truth(用户测试):ActiveRecord::StatementInvalid:PGError:ERROR:关系“技术人员”不存在:从“技术人员”中删除...从本质上讲,标准框架不承认Technicians和Users表(或PostgreSQL称它们为“关系”)不存在,事实上,应该别名为Accounts。有什么想法吗?我对RoR比较陌生,不知道如何解决这个问题而又不完全删除STI。 最佳答案 原来问题是由于存在:./te
假设我有一个名为Flight的模块,其中包含类方法和实例方法。我可以使用include、extend或两者将其方法放入类中:classBatinclude会将Flight添加到Bat.ancestors,但extend不会。我的问题是,为什么模块与类不同?当我对Mammal进行子类化时,我总是同时获得类和实例方法。然而,当我混入一个模块时,我不能同时获得类和实例方法(除非我使用self.included钩子(Hook)或类似ActiveSupport::Concern的东西)。这种差异背后是否存在语言设计问题? 最佳答案 Modul
在Ruby(1.8.X)中为什么Object既继承了内核又包含了内核?仅仅继承还不够吗?irb(main):006:0>Object.ancestors=>[Object,Kernel]irb(main):005:0>Object.included_modules=>[Kernel]irb(main):011:0>Object.superclass=>nil请注意,在Ruby1.9中情况类似(但更简洁):irb(main):001:0>Object.ancestors=>[Object,Kernel,BasicObject]irb(main):002:0>Object.included
我正在为Rails创建我的第一个插件。我对ruby还是很陌生,我想知道是否有可能获得继承类?例如,我正在尝试创建一个插件,在您不使用迁移时允许进行单元测试和功能测试。我要做的是初始化一个名为controller的类变量,以初始化为正在测试的Controller类型。如果我有一个基类ControllerTest:classControllerTest所以我目前坚持的是获取继承类的名称。这可能吗?如果没有,有没有人知道我可以如何着手实现它的另一种方式?提前致谢。 最佳答案 非常简单:使用“继承”回调。来自Class类的RDoc:in