以下内容为自学笔记,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!
一、CLasses 类中基本内容
- Dart是面向对象的编程语言,支持基于mixin的集成机制。
- 可以通过构造函数来创建新的对象。可使用
new
关键字,也可不使用。构造函数名字可以为ClassName
或ClassName.identifier
var list1 = new List();
var list2 = List();
var list3 = List.from(Iterable.empty());
-
Dart语言的类中的构造函数可分以下形式:
- 默认构造函数
- 命名构造函数
- 超类构造函数
- 重定向构造函数
- 工厂方法构造函数
-
Dart语言中类分为:
- 抽象类 Abstract classes
- 隐式接口 Implicit interfaces
- 扩展类 Extending a class
- 枚举类型 Enumerated types
-
Dart语言的类中包含变量和函数
二、构造函数
1、默认构造函数
- 如果你没有定义构造函数,则会有个默认构造函数。默认构造函数没有参数,并且会调用超类的没有参数的构造函数。
- 子类不会继承超类的构造函数。子类如果没有定义构造函数,则只有一个默认构造函数(没有名字没有参数)。
2、常规构造函数
使用 new
关键字和构造函数来创建新的对象
//java中写法
class Point1 {
num x;
num y;
Point1(num x, num y) {
this.x = x;
this.y = y;
}
}
//dart建议写法
class Point2 {
num x;
num y;
Point2(this.x, this.y);
}
main(){
var p1 = new Point1(2, 2);
var p2 = new Point2(2, 2);
}
3、常量构造函数
a、const和final的区别
final
修饰的变量只能初始化一次,但不要求赋的值为编译时常量;而const
要求在声明时就初始化,并且初始化必须为编译时常量。class Test1{ //const修饰成员变量时必须与static同时使用,编译时常量即在编译时就确定了值, //bool,字符串,数字,list的字面量形式都是编译时常量 static const a = true; static const int b = 1; static const c = '字符串'; static const b = [1,2,3]; static const e = b + 2;//其中b也要是const常量 }
final
是惰性初始化,即在运行时第一次使用前才初始化。class Test{ //可以通过以下两种方式初始化,但都是在运行时才会执行 final a ; final b = 1;//可直接初始化 //可以通过下面方式初始化 Test(this.a){} Test(a) : this.a= a{} }
const
可以修饰变量,也可以修饰值,而final
只用来修饰变量。void test2(){ //const修饰局部变量时,直接使用关键字即可。 //const既可以修饰变量,也可以修饰常量。 const list1 = [1,2,3]; const list2 = const [1,2,3]; // error, 因为new DateTime.now()不是编译时常量 // const list3 = const [new DateTime.now(), 2, 3]; //final只能修饰变量 final list6 = [1, 2, 3]; // final list7 = final [1, 2, 3]; //error final list8 = const [1, 2, 3]; //const无论修饰变量还是值,后边都必须接编译时常量 var x1 = 5; // const list4 = const[x1]; //error,x1不是const const x2 = 5; const list5 = const[x2]; // error, 因为new DateTime.now()不是const // final list9 = const [new DateTime.now(), 2, 3]; }
总结:const 修饰值的时候,要求值必需是常量或由常量组成。var、final等在左边定义变量时,并不关心右边是不是常量。但如果右边用了const,那么不管左边是什么要求,右边都必需是常量。
b、常量构造函数
- 常量构造函数可以提供一个状态不变的对象,此对象为编译时常量,要使用常量构造函数只需要用
const
替代new
即可: - 两个一样的编译时常量其实是同一个对象:
- 定义一个
const
构造函数,类的所有成员变量都要为final
或const
修饰的。 - 可以使用
new
调用const
构造函数,但这样就不能使用const
声明变量了。 - const 构造函数也不能有函数体。
class Point2 {
final num x;
final num y;
static const num z = 9;
const Point2(this.x, this.y);
}
main(){
var p1 = const Point2(2, 2);//ok
const p2 = const Point2(2, 2);//ok
// const p3 = new Point2(2,2);//error
final p4 = const Point2(2,2);//ok
final p5 = new Point2(2,2);//ok
assert(identical(p1, p2));//判断是否是同一个实例对象 true
assert(identical(p1, p4));//判断是否是同一个实例对象 true
assert(identical(p1, p5));//判断是否是同一个实例对象 false
}
4、命名构造函数
- Dart语言不支持方法重载。构造函数的重载也不支持,但支持命名构造函数。函数格式:
ClassName.identifier
,类名.标识符(即方法名)。 - 使用命名构造函数,可以为一个类实现多个构造函数,能更清晰的表明你的意图。
- 构造函数不能继承,所以超类的命名构造函数也不会被继承。如果你希望子类也有超类一样的命名构造函数,你必须在子类中自己实现该构造函数。
class Point {
num x;
num y;
//常规的构造函数
Point(this.x, this.y);
// 此处是命名构造函数
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
5、调用超类构造函数
- 默认情况,子类构造函数会自动调用超类的无名无参的默认构造函数。
- 超类的构造函数在子类构造函数体开始执行的位置调用。
- 如果提供了一个
initializer list
(初始化参数列表),则初始化参数列表在超类构造函数执行之前执行。 - 如果超类没有无名无参构造函数,则需要手工调用超类的其他构造函数。
- 调用超类构造函数时,在子类构造函数参数后使用冒号
:super
调用。 - 构造函数的执行顺序如下:
- initializer list 初始化参数列表
- superclass’s no-arg constructor 超类的无名构造函数
- main class’s no-arg constructor 主类的无名构造函数
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person父类没有默认构造函数;
// 这里必须调用父类的构造函数 super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// 此处打印结果:
// in Person 此处先执行父类的构造函数
// in Employee 然后执行子类的构造函数
}
注:调用超类构造函数的参数无法访问
this
。例如,参数可以为静态函数但是不能是实例函数。
initializer list 初始化列表
- 如果在构造函数的初始化列表中使用
super()
,需要把它放到最后。 - 在构造函数体执行之前除了可以调用超类构造函数之外,还可以初始化实例参数。使用逗号分隔初始化表达式。
- 初始化列表非常适合用来设置 final 变量的值。
import 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
// Initializer list sets instance variables before
// the constructor body runs.
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
//3.605551275463989
**警告:**初始化表达式等号右边的部分不能访问
this
。
6、重定向构造函数
- 有时候一个构造函数会调动类中的其他构造函数。此时就是重定向函数。
- 在构造函数声明后,使用冒号
: this.xxx
调用其他构造函数。 - 一个重定向构造函数是没有代码的,即不允许有代码块
{}
。
class Point {
num x;
num y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
7、工厂方法构造函数
- 如果一个构造函数并不总是返回一个新的对象,则使用
factory
来定义这个构造函数。 - 实现单例可以使用工厂方法构造函数
class A{
String name;
//注意: 工厂构造函数无法访问 this。所以这儿要静态的
static A _cache;
//工厂构造函数的关键字为factory
factory A([String name = 'A']){
if(A._cache == null){
A._cache=new A._newObject(name);
}
return A._cache;
}
//需要一个命名构造函数用来生产实例
A._newObject(this.name);
}
void goFactory(){
A a = new A('HelloWorld');
print(a.name);
A b = new A('HelloDart');
print(b.name);
print(a == b);
}
//打印结果
HelloWorld
HelloWorld
true
三、get和set 方法
-
在Dart语言中,每个实例变量都隐含的具有一个
get
方法,如果变量不是fianl
的则还有个set
方法。 -
在Dart中声明变量有两种方法,但调用方法一样。
- 直接声明
var temp ;
,此时会有默认的get
和set
方法与之对应;
num a;
- 使用
get
和set
方法声明。
num get c { //自定义实现,不影响调用处 return a + b; } set c(num value) { //自定义实现,不影响调用处 a = value + b; }
- 直接声明
** 注:**两种方式不可同时使用,否则会报:
Error: 'c' is already declared in this scope.
错误。
-
在使用过程中,当执行
var temp = 实例.变量;
这句时,就调用了变量的get
方法;当执行实例.变量 = 5;
时,就调用了变量的set
方法。 -
基于第3点,此时修改变量的set和get方法的内部实现时,调用变量的代码不需要修改。
class GetAndSet {
num a;
num b;
GetAndSet(this.a, this.b);
//声明变量c 的get方法
num get c {
print('get c');
return a + b;
}
//声明变量c的set方法
set c(num value) {
print('set c');
a = value + b;
}
}
main() {
var i = new GetAndSet(4, 7); //此时 a = 4 ,b = 7;
i.c = 4;//此时调用了set c a = 4 + b = 11 ;b = 7 ,c = 18;
var k = i.c;//此时调用了get c a = 11 ,b = 7 ,c = a + b = 18 ,k = c =18
print('i.a = ${i.a},i.b = ${i.b},i.c = ${i.c},k = $k');
}
//打印结果
//set c
//get c
//get c 这里是print中i.c打印的
//i.a = 11,i.b = 7,i.c = 18,k = 18
四、抽象类
- 含有抽象方法的类就是抽象类,使用关键字
abstract
修饰类。 - 抽象方法是用
;
代替函数体也就是没有大括号{}
。run();
- 抽象方法只能在抽象类中出现,子类必须实现抽象方法。
- 抽象类中也可以有普通方法。
abstract class Car {
run();
space() {
print('普通方法!');
}
}
class Bus extends Car {
int width = 4;
Bus(this.width);
//必须复写抽象方法,也可空实现
@override
run() {
print('Bus 起飞跑');
}
}
五、接口
- 在Dart中所有的类都是一个接口。类中隐式的定义了一个包含所有实例成员的接口。
- 在如果你想创建类A来支持类B的api,但不想继承B的实现,那么就实现B的接口。
- 一个类可以使用关键字
implements
实现一个或多个接口,并要实现接口中所有成员变量和函数。
abstract class Car {
run();
space() {
print('car的普通方法!');
}
}
class Bus extends Car {
int width = 4;
Bus(this.width);
//必须复写抽象方法,也可空实现
@override
run() {
print('Bus 起飞跑');
}
}
class C implements Bus {
//成员变量也要实现
@override
int width;
//从新实现 接口重写父类的抽象方法
@override
run() {
print('C 的 run方法!');
}
//从新实现 接口继承父类的方法
@override
space() {
print('C 的space方法!');
}
}
main() {
var c = new C();
c.run();
c.space();
}
//打印结果 接口实现的方法不会有父类的实现
C 的 run方法!
C 的space方法!
六、mixins机制
-
mixin是一种在多类继承中,重用一个类代码的方法。
-
使用关键字
with
后面接一个或多个类。 -
mixin类不可以含有构造函数。
-
mixin与接口不同都是,传统接口不包含实现部分,而mixin中是包含实现部分的。
-
mixin与传统的继承和接口混合使用的区别:
- 传统方式对于相同方法执行的优先级是:优先执行继承类,如果继承中没有,则执行实现类中的方法。
class S { a() { print("S.a"); } b() { print("S.b"); } c() { print("S.c "); } } class A { a() { print("A.a"); } b() { print("A.b"); } } class B { a() { print("B.a"); } //如果不注释此方法,则打印结果都为 B.a B.b B.c // b() { // print("B.b"); // } c() { print("B.c "); } } class T extends B implements A,S{ @override b() { print("T.b"); } } main() { T t = new T(); t.a(); t.b(); t.c(); assert(t is S);//ture } //打印结果 B.a T.b B.c
- mixin机制的优先级是:with关键字后越靠后的优先级越高
class S { a() { print("S.a"); } b() { print("S.b"); } c() { print("S.c "); } } class A { a() { print("A.a"); } b() { print("A.b"); } } class B { a() { print("B.a"); } b() { print("B.b"); } c() { print("B.c "); } } class T extends B with A,S{ } // class T = B with A, S; 可以写成这样 main() { T t = new T(); t.a(); t.b(); t.c(); assert(t is S);//ture } //打印结果 S.a S.b S.c //如果注释了S中的b方法,则打印结果为 S.a A.b S.c
注意: 从 Dart 1.13 开始, 这两个限制在 Dart VM 上 没有那么严格了:
- Mixins 可以继承其他类,不再限制为继承 Object
- Mixins 可以调用
super()
。
总结:
mixin机制可以为类增加新的功能,使用with 将前后类中的相同方法通过优先级挑选出来与各自的不同方法,从新综合到一个新类中。如下:
- 定义各个mixin类
class S {
a() {
print("S.a");
}
}
class A {
a() {
print("A.a");
}
b() {
print("A.b");
}
}
class B {
a() {
print("B.a");
}
b() {
print("B.b");
}
c() {
print("B.c ");
}
}
- 使用with
// class T = B with A, S;与下列方式一样
class T extends B with A,S{}
//此时的T类就相当于是
class T{
a() {
print("S.a");
}
b() {
print("A.b");
}
c() {
print("B.c ");
}
}
- 使用
main() {
T t = new T();
t.a();
t.b();
t.c();
assert(t is S);//ture
}
//打印结果
S.a
A.b
B.c