前言
这里是前端学习Dart第七节,本节知识点:类的相关知识。
视频地址:待上传
类
前面我们已知Dart是一种面向对象的语言,类对于Dart来说非常重要。
看一下Dart中class声明语法:
类的名称需符合前面章节中提到过的标识符命名规则。
类的定义包含一下内容
<fields>字段 - 字段是类中声明的任何变量。字段表示与对象有关的数据。<getters/setters>get和set方法 - 允许程序初始化和检索类字段的值。默认的getter/setter与每个类相关联。但是,可以通过显式定义setter/getter来覆盖默认值。<constructors>构造函数 - 负责为类的对象分配内存。<functions>函数 - 函数表示对象可以采取的操作。它们有时也被称为方法。
创建类的实例
如上代码,声明了Car类,Car类中有成员变量model, 成员函数printModel。随后main函数中进行了Car类的实例化。
实例化一个类的话需要new关键字(Dart2.0版本之后可以舍弃new关键字)。类实例化语法如下:
可以通过对象访问类的属性和函数。使用.用于访问类的数据成员的点表示法(成为句点)。
类的成员变量
类中声明的变量即为类的成员变量。
类的实例可以访问类的成员变量。
所有实例变量都生成隐式 getter 方法。 非 final 的实例变量同样会生成隐式 setter 方法。
类的成员变量初始化赋值的过程在构造函数及其初始化列表之前。初始化列表即为下文中冒号操作符(:)。
构造函数
构造函数是类中的特殊函数,负责初始化类的变量,构造函数是一个函数,因此可以参数化。但是构造函数不能具有返回值,如果一个类未声明构造函数,默认提供无参数的构造函数。
Dart类包含两种构造函数,同名构造函数、命名构造函数。
同名构造
同名构造函数是最常见的构造函数形式。
可以看到类中包含this关键字,这里关联一下上下文,前面章节中有提及一句Dart中没有this,然而类这里有出现了,这是为什么呢?
近当存在命名冲突时,使用
this关键字。 否则,按照 Dart 风格应该省略this。
如果命名不冲突,如上声明也是可以的。
构造函数有一种语法糖,可以精简的进行变量赋值。
可以理解为同名赋值的简化形式,这种形式也支持占位命名参数(可以回顾一下函数章节,参数模块)
几点注意事项:
- 在没有声明构造函数的情况下,Dart会提供一个默认的构造函数。默认构造函数没有参数并会调用父类的无参构造函数
- 子类不会继承父类的构造函数。子类不声明构造函数,那么它就只有默认构造函数(匿名,没有参数)
命名构造函数
命名构造函数可以为一个类实现多个构造函数,可以使用命名构造函数来更清晰的表明函数意图:
那么问题来了,命名构造函数可不可以被继承呢?可以试一下
命名构造函数不会被子类继承。
换一个情形,如果类没有默认构造函数,但是有命名构造函数。那么实例化的时候必须使用一种命名构造进行实例化。
继承子类,如果想要调用父类的命名构造函数,可以使用冒号,位置在函数体之前。
上图中,super.origin() 命令执行在 ThreePoint()默认构造函数之前,super.origin即调用Point类中的origin命名构造函数。
冒号和命名构造函数体之间不仅可以调用super.xx函数,还可以直接初始化实例变量。
可以看到super.xx函数前面插入了z变量的初始化,多个变量初始化可以逗号分隔,super函数必须放在最后一位。
还可以插入断言。
常量构造函数
如果类生成的对象是固定不变的,那么就可以把这些对象定义为编译时常量。为此可以定义一个const构造函数,并且声明所有实例变量为final
如果你的类,创建的对象永远不会改变,你可以在编译期就创建这个常量实例,并且定义一个常量构造函数,并且确保所有的成员变量都是final的。
因为是const类型,常量构造函数创建的实例是同一个标准实例。
在常量上下文中,构造函数作者字面量前的const可以省略。
工厂构造函数
当执行构造函数并不总是创建这个类的一个新实例时,则使用factory关键字。概念可以理解为多态的单例。
工厂构造函数无法访问this。
类的成员函数
在类中声明的函数即类的成员函数
上图中origin即为Point类的成员函数。
类的实例可以访问类的成员函数
Getter 和 Setter
Getter和Setter用于对于对象属性读和写的特殊方法。
未声明Getter和Setter的成员变量都有一个隐式的Getter,通常情况下还会有Setter,非通常的情况为final类型。
类的static
static的作用也是声明类的成员变量或者成员函数,使多个相同类型的类对象,共享同一个成员变量的实例。
static修饰的成员变量成为静态变量,静态变量只有它们被用到的时候才会初始化。
相应的静态函数也可以如上声明,静态函数不能在实例中使用,因此它们不能访问this。
静态函数可以当做编译时常量使用。 例如,可以将静态方法作为参数传递给常量构造函数。
抽象类
抽象类通过abstract修饰符来装饰,抽象类不可实例化。抽象类通常定义接口或者部分实现。
抽象类中包含抽象方法, 抽象方法的定义为没有函数体的方法。
上面eat方法就是一个抽象方法,后面的函数体被省略掉,具体如何实现交给子类。
可以子类中实现了eat方法,猫继承(extends)自Animal抽象类。并且实现了抽象类的抽象方法。
接口
Dart中的接口也是通过abstrack关键字声明的,其他语言类似包含interface。
还可以实现多接口
Extends & Implements
上述接口与抽象类的例子中分别使用类extends 与 implements, 我们大体理解为两者都是“继承”的某种实现,那他们有什么区别呢?
Dart中只可以单继承,使用extends关键字。
extends继承的几个特点
- 子类可以继承父类中可见的属性和方法
- 子类可以通过super关键字调用父类的方法。
- 子类不会继承父类的构造函数
如果 A extends B 的话,如果B是普通类,A中可以使用B中所有可见的函数和参数(不包含构造函数)。
如果B是抽象类,那么A中必须实现B中的抽象函数,因为B中的抽象函数没有函数体。
如果 A implements B ,无论B是普通类还是抽象类,A中必须实现B中的所有属性和方法,B中的函数体和属性值都不会被继承过来,A把B当成了类的模板,只继承结构,不继承实现。所以这个概念叫做接口。
implements另一个特点就是允许后面接上多个类。
Mixin
Mixin 是在多个类层次结构中重用类代码的一种方法,Mixin解决了“多继承”的问题。
上面是一个比较宏观的概念,下面我们白话一点讲。
mixin用于修饰类,和abstract类似。用于声明一个类模块。
如上声明了一个Brid mixin。其中包含action属性、fly成员函数。
声明角度来看,mixin类可以生命成员变量,成员函数,抽象方法。但是不可以进行实例化。
这时给别的类“安装”上这个mixin,使用with关键字。
可以看到输出为I can fly。Dog类使用了Brid mixin类模块中的成员函数。
类可以 with 多个 mixin,逗号分隔。
这样Dog又会飞又会跳了。
mixin可以限制使用范围,限定Brid mixin只能用在Animal类及其子类上,使用on关键字。
并且上述例子中,Brid mixin中可以使用 Animal 类中的成员。
类with的并不一定是mixin类,可以是普通类、抽象类。
用一个例子类体会一下mixin是如何解决“多继承”问题的。
END
在本章节中我们学习了知识点:类的基本概念,implements、extends、mixins等相关类的概念。