前言
前几篇文章分别讲解了Dart中的变量、内置类型、函数(方法)、操作符、流程控制语句和异常,对Dart的基本语法已经有了很多的了解,那么今天来说一下Dart中的类。前几篇文章没有看的,建议先看一下前几篇文章。
那些你不知道的Dart细节之变量
那些你不知道的Dart细节之内置类型
那些你不知道的Dart细节之函数(方法)
那些你不知道的Dart细节之操作符、流程控制语句、异常
那些你不知道的Dart细节之类的点点滴滴
那些你不知道的Dart细节之泛型和库
构造函数
前几篇文章中在讲函数(方法)的一篇中提到过,这里再说一下吧,首先来看一下Java中构造函数的写法:
1
2
3
4
5
6
7
8
9
10
11 1class Point {
2 double x;
3 double y;
4
5 Point(int x, int y) {
6 this.x = x;
7 this.y = y;
8 }
9}
10
11
下面是dart中的建议写法:
1
2
3
4
5
6
7 1class Point {
2 num x;
3 num y;
4 Point(this.x, this.y);
5}
6
7
命名构造函数
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1class Point {
2 num x;
3 num y;
4
5 Point(this.x, this.y);
6
7 //命名构造函数
8 Point.fromJson(Map json) {
9 x = json['x'];
10 y = json['y'];
11 }
12}
13
14
重定向构造函数
一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
1
2
3
4
5
6
7
8
9
10
11 1class Point {
2 num x;
3 num y;
4
5 Point(this.x, this.y);
6
7 //重定向构造函数,使用冒号调用其他构造函数
8 Point.alongXAxis(num x) : this(x, 0);
9}
10
11
初始化列表
在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。初始化列表非常适合用来设置 final 变量的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 1import 'dart:math';
2
3class Point {
4 //final变量不能被修改,必须被构造函数初始化
5 final num x;
6 final num y;
7 final num distanceFromOrigin;
8
9 //初始化列表
10 Point(x, y)
11 : x = x,
12 y = y,
13 distanceFromOrigin = sqrt(x * x + y * y);
14}
15
16
调用超类构造函数
首先来建立一个超类(父类),下面的代码都继承自此类:
1
2
3
4
5
6
7
8
9
10
11
12
13 1class Parent {
2 int x;
3 int y;
4
5 //父类命名构造函数不会传递
6 Parent.fromJson(x, y)
7 : x = x,
8 y = y {
9 print('父类命名构造函数');
10 }
11}
12
13
-
超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。
1
2
3
4
5
6
7
8
9
10
11
12 1class Child extends Parent {
2 int x;
3 int y;
4
5 Child.fromJson(x, y)
6 : x = x,
7 y = y,
8 print('子类命名构造函数');
9 }
10}
11
12
-
如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数,调用超类构造函数的参数无法访问 this。
1
2
3
4
5
6
7
8
9
10
11
12 1class Child extends Parent {
2 int x;
3 int y;
4 //若超类没有默认构造函数, 需要手动调用超类其他构造函数
5 Child(x, y) : super.fromJson(x, y) {
6 //调用父类构造函数的参数无法访问 this
7 print('子类构造函数');
8 }
9}
10
11
12
-
在构造函数的初始化列表中使用 super(),需要把它放到最后。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1class Child extends Parent {
2 int x;
3 int y;
4
5 Child.fromJson(x, y)
6 : x = x,
7 y = y,
8 super.fromJson(x, y) {
9 print('子类命名构造函数');
10 }
11}
12
13
14
常量构造函数
定义const构造函数要确保所有实例变量都是final。
const关键字放在构造函数名称之前。
1
2
3
4
5
6
7
8
9
10
11 1class Point2 {
2 //定义const构造函数要确保所有实例变量都是final
3 final num x;
4 final num y;
5 static final Point2 origin = const Point2(0, 0);
6
7 //const关键字放在构造函数名称之前,且不能有函数体
8 const Point2(this.x, this.y);
9}
10
11
工厂构造函数(Dart中的单例)
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。如果一个构造函数并不总是返回一个新的对象(单例),则使用 factory 来定义这个构造函数。工厂构造函数无法访问this。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1class Singleton {
2 String name;
3 //工厂构造函数无法访问this,所以这里要用static
4 static Singleton _cache;
5
6 //工厂方法构造函数,关键字factory
7 factory Singleton([String name = 'singleton']) =>
8 Singleton._cache ??= Singleton._newObject(name);
9
10 //定义一个命名构造函数用来生产实例
11 Singleton._newObject(this.name);
12}
13
14
Setter和Getter
在Java中get和set方法可以直接生成,在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 1class Rectangle {
2 num left;
3 num top;
4 num width;
5 num height;
6
7 Rectangle(this.left, this.top, this.width, this.height);
8
9 num get right => left + width;
10 set right(num value) => left = value - width;
11 num get bottom => top + height;
12 set bottom(num value) => top = value - height;
13}
14
15
抽象类(接口)
- 不能被实例化,除非定义一个工厂构造函数。
这个很好理解,在Java中的抽象类也同样不可实例化。
- 抽象类通常用来定义接口, 以及部分实现。
在Dart中没有interface这个关键字,只有abstract,所以可以使用abstract来生成接口:
1
2
3
4
5
6
7
8
9
10 1abstract class Demo{
2 void zhujiang();
3}
4
5class Zhu implements Demo{
6 @override
7 void zhujiang() {}
8}
9
10
- 抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。
其实上面代码也可以用作这个实例,只需要把implements变成extends即可:
1
2
3
4
5
6 1class Zhu extends Demo{
2 @override
3 void zhujiang() {}
4}
5
6
-
接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。
-
一个类可以implement一个普通类。Dart任何一个类都是接口。
-
一个类可以implement多个接口。
最后几个说的其实有点绕,说白了就是可以实现多个,这里需要注意的是:implement只是实现接口,你需要重写接口中的方法,不然是不会执行的。还有一点是如果想extents多个类的话需要使用with关键字。说了这么多不如直接看代码,看代码应该好理解一些:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1abstract class Demo{
2 void zhujiang();
3}
4
5abstract class Demo2{
6 void zhujiang();
7}
8
9abstract class Demo3{
10 void zhujiang();
11}
12
13class Zhu extends Demo with Demo2,Demo3 implements Demo3,Demo2{
14 @override
15 void zhujiang() {}
16}
17
18
可调用类
实现call()方法可以让类像函数一样能够被调用。这个很简单,直接上代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1class ClassFunction {
2 call(String a, String b, String c) => '$a $b $c!';
3}
4
5main() {
6 var cf = new ClassFunction();
7 var out = cf("aaa","flutter","damon");
8 print('$out');
9 print(cf.runtimeType);
10 print(out.runtimeType);
11 print(cf is Function);
12}
13
14
下面是打印结果:
1
2
3
4
5
6
7 1lib/5.dart: Warning: Interpreting this as package URI, 'package:darttest/5.dart'.
2aaa flutter damon!
3ClassFunction
4String
5false
6
7
Mixin
- 子类没有重写超类A方法的前提下,如果2个或多个超类拥有相同签名的A方法,那么子类会以继承的最后一个超类中的A方法为准。
- 如果子类自己重写了A方法则以本身的A方法为准。
在这里先不过多解释Minxin,我的理解就类似于策略模式,抽出不变的为接口,然后多实现。这里先不写了,如果有可能的话等专门写一篇介绍Dart中的Minxin的文章吧。
总结
到这里为止Dart的第五篇文章完成。本篇文章主要讲解了一下Dart语言的类。下一篇文章讲解一下Dart中的泛xing和导入库,希望大家能够喜欢,对大家如果有帮助的话,欢迎点赞关注,如果有异议,可以留言进行讨论。