title: Dart 类和对象date: 2021-07-15 13:16:58.734
updated: 2021-07-28 12:00:10.149
url: /?p=301
categories: Dart
tags:
类和对象
Dart 是一个面向对象编程语言。 每个对象都是一个类的实例,所有的类都继承于 `Object`。
1 2 3 4 5
| class Point { num x; num y; }
|
私有属性
变量名使用_
加变量名的形式,比如:_varName
。
1 2 3 4
| class Point { num _x; num _y; }
|
私有类
和私有属性差不多,使用_
加类名:比如_className
。
1 2 3 4
| class _Point { num x; num y; }
|
构造函数
由于把构造函数参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作:
1 2 3 4 5 6
| class Point { num x; num y;
Point(this.x, this.y); }
|
可选参数列表的构造方法。
1 2 3 4 5 6
| class Point { num x; num y;
Point([this.x, this.y]); }
|
命名构造函数
Dart 并不支持构造函数的重载,而采用了命名构造函数为一个类实现多个构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Point { num x; num y; Point(this.x, this.y); Point(this.y); Point.y(this.y) { x = 0; } }
var p = Point.y(0);
|
注意了,我们的构造函数是支持可选参数的,为什么还要支持命名构造函数,我个人认为这是不冲突的,命名构造方法主要是命名,我们通过命名知道该构造函数的具体作用,说白了就是给构造函数取个名字,重载构造函数也算是它的附加能力罢了。
初始化列表
在构造函数函数体执行之前会首先执行初始化列表,非常适合用来设置 final 变量的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Point { num x; num y; Point(this.x, this.y); Point.y(this.y) { x = 0; }
Point.fromMap(Map map) : x = map['x'], y = map['y']; Point.x(int i) : x = i, y = 0; }
|
重定向构造函数
有时候一个构造函数会调动类中的其他构造函数(在Java中就是 `this(...)`)。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
1 2 3 4 5 6 7
| class Point { num x; num y;
Point(this.x, this.y); Point.xy(int x,int y):this(x,y); }
|
常量构造函数
如果你的类提供一个状态不变的对象,你可以把这些对象 定义为编译时常量。要实现这个功能,需要定义一个 `const` 构造函数, 并且声明所有类的变量为 `final`。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); }
void main(){ var p1 = const ImmutablePoint(0,0); var p2 = const ImmutablePoint(0,0); print(p1 == p2); }
|
工厂构造函数
当实现一个使用`factory` 关键词修饰的构造函数时,这个构造函数不必创建类的新实例。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。(工厂构造函数无法访问 `this`)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Logger { final String name; static final Map _cache = {}; factory Logger(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final logger = Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name); }
|
借助工厂构造函数能够实现单例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Manager { static Manager _instance; factory Manager.getInstance() { if (_instance == null) { _instance = new Manager._internal(); } return _instance; }
Manager._internal(); }
|
Getters 和 Setters(访问器和设置器)
Dart中每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。可以通过实现 getter 和 setter 来创建新的属性, 使用 get
和 set
关键字定义 getter 和 setter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Rect { num left; num top; num width; num height;
Rect(this.left, this.top, this.width, this.height);
num get right => left + width; set right(num value) => left = value - width; }
void main() { var rect = Rect(0, 0, 10, 10); print(rect.right); rect.right = 15; print(rect.left); }
|
需要注意的是,在get与set中使用自身会导致Stack Overflow
操作符重载
把已经定义的、有一定功能的操作符进行重新定义。可以重新定义的操作符有:
| <
| +
| |
| []
|
| — | — | — | — |
| >
| /
| ^
| []=
|
| <=
| ~/
| &
| ~
|
| >=
| *
| <<
| ==
|
| –
| %
| >>
| |
比如:List就重写了 []
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Point { int x; int y; Point operator +(Point point) { return Point(x + point.x, y + point.y); }
Point(this.x, this.y); }
var p1 = Point(1, 1); var p2 = p1 + Point(2, 2); print(p2.x); print(p2.y);
|
抽象类
使用 `abstract` 修饰符定义一个抽象类。抽象类中允许出现无方法体的方法
1 2 3 4
| abstract class Parent { String name; void printName(); }
|
抽象类不能被实例化,除非定义工厂方法并返回子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| abstract class Parent { String name; Parent(this.name); factory Parent.test(String name){ return new Child(name); } void printName(); }
class Child extends Parent{ Child(String name) : super(name);
@override void printName() { print(name); } }
void main() { var p = Parent.test("Lance"); print(p.runtimeType); p.printName(); }
|
接口
与Java不同,Dart中没有`interface`关键字,**Dart中每个类都隐式的定义了一个包含所有实例成员的接口**, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 方法,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Listener{ void onComplete(){} void onFailure(){} }
class MyListsner implements Listener{ MyListsner(){
} @override void onComplete() { }
@override void onFailure() { } }
|
与继承的区别在于:
1、单继承,多实现。
2、继承可以有选择的重写父类方法并且可以使用super
,实现强制重新定义接口所有成员。
可调用的类
如果 Dart 类实现了 `call()` 函数则 可以当做方法来调用。
1 2 3 4 5 6 7 8 9
| class Closure { call(String a, String b) => '$a $b!'; }
main() { var c = new Closure(); var out = c("Hello","Dart"); print(out); }
|
混合mixins
Mixins 是一种在多类继承中重用 一个类代码的方法。它的基本形式如下:
1 2 3 4 5 6 7 8 9 10
| class A { void a(){} } class B{ void b(){} } class C with A,B{ void c(){} }
|
如果类C并没有自己的方法,还可以简单的声明类C,如下形式:
1 2 3 4 5 6 7 8
| class A { void a(){} } class B{ void b(){} } class C = Object with A,B;
|
with
后面跟着需要混入的类,被mixin
(混入)的类不能有构造函数。现在的 C
拥有了三个方法(a、b与c)。假设A与B 存在相同的方法,以最右侧的混入类为主,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class A { String getMessage() => 'A'; }
class B { String getMessage() => 'B'; }
class AB with A, B {}
class BA with B, A {}
void printMessage(obj) => print(obj.getMessage());
void main() { printMessage(AB()); printMessage(BA()); }
|
继承与mixins是兼容的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class A { String getMessage() => 'A'; }
class B { String getMessage() => 'B'; } class P{ String getMessage() => 'P'; } class AB extends P with A, B {}
class BA extends P with B, A {}
void printMessage(obj) => print(obj.getMessage());
void main() { printMessage(AB()); printMessage(BA()); }
|
mixins弥补了接口和继承的不足,继承只能单继承,而接口无法复用实现,mixins却可以多混入并且能利用到混入类的具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| abstract class Swimming{ void swimming(){ print("游泳"); } }
abstract class Jump{ void jump(){ print("跳跃"); } }
class Lance extends Swimming implements Jump{ void jump(){ print("跳跃"); } }
class Lance1 with Swimming, Jump {}
|
with与extend具体区别?