一、运算符
Dart中的基本运算符和其他语言无差异,但有几个运算符是其他平台没有的
1.1. 除法、整除、取模运算
1 | var num = 7; |
1.2. ??=赋值运算
- 当变量有值时,使用自己原来的值。
- 当变量为null时,使用后面的内容进行赋值。
1
2
3
4
5main(List<String> args) {
var name = 'idbeny';
name ??= '1024星球';
print(name); // 当name初始化有值时(idbeny),结果为初始化值,当初始化为null时,取后面的值(1024星球)
}
1.3. 条件运算符
- 如果expr1非null,直接使用expr1的结果。
- 如果expr1是null,则返回expr2的结果;
1
2
3
4var name = 'idbeny';
var name = null;
var result = name ?? '1024星球';
print(name);
赋值运算符和条件运算符很容易混淆;
??=
类似于三目运算符;??
就是一个条件判断
1.4. 级联语法(..)
场景:不需要新创建对象,直接连续操作对象
1 | class Person { |
二、逻辑判断
和其他语言用法一样,但是if else
语句和其他平台不一样:不存在非空即真或者非0即真,必须有明确的bool类型
三、类和对象
3.1. 定义类和对象
格式:
1 | class 类名 { |
示例:
1 | // 1.创建对象 |
注意点:
- 在方法中使用成员变量并没有加this;
- 在方法中通常使用成员变量时,会省略this,但是有命名冲突时,this不能省略;
- 从Dart2开始,new关键字可以省略。
3.2. 构造方法
普通构造方法
- 当通过类创建一个对象时,会调用这个类的构造方法。没有明确指定构造方法时,将默认拥有一个无参的构造方法。
- 当自定义构造方法时,默认的构造方法将会失效;
- Dart不支持函数的重载。
- 参数赋值的语法糖:
1
2
3
4
5
6Person(String name, int age) {
this.name = name;
this.age = age;
}
// 等同于
Person(this.name, this.age);
命名构造方法(解决函数重载问题)
1 | class Person { |
初始化列表
官方很多示例程序及源码大量使用了初始化列表,在创建一个构造方法时经常用到
1 | const defaultAge = 28; |
重定向构造方法
在一个构造方法中去调用另外一个构造方法, 这个时候可以使用重定向构造方法:
1 | class Person { |
注意:冒号后面使用this调用
常量构造方法
场景:相同的参数创建相同的对象
1 | main(List<String> args) { |
- 构造方法用const修饰,那么可以保证同一个参数,创建出来的对象是相同的
- 常量构造方法的类中,所有的成员变量必须是final修饰的.
- 变量用const修饰时,对象const可以省略.
工厂构造方法(常量构造方法升级版)
1 | main(List<String> args) { |
3.3. setter和getter
- 默认情况下,Dart中类定义的属性是可以直接被外界访问的。
- 如果要监听类的属性变化,使用set和get
1 | main(List<String> args) { |
3.4. 类的继承
- 使用extends关键字,子类中使用super访问父类。
- 父类中的所有成员变量和方法都会被继承(构造方法除外)。
- 子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写:
- 子类的构造方法在执行前,将隐含调用父类的默认构造方法(无参)
- 如果父类没有默认构造方法,则子类的构造方法必须在初始化列表中通过super显式调用父类的某个构造方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24main(List<String> args) {
var p = new Person();
p.age = 28;
p.run();
}
class Animal {
int age;
run() {
print('继承在奔跑');
}
}
class Person extends Animal {
String name;
Person(String name, int age) : name=name, super(age);
run() {
print('$name在奔跑');
}
}
3.5. 抽象类
父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,我们可以定义为抽象方法。
- 抽象方法定义:没有具体实现的方法。
- 抽象方法必须存在于抽象类中。
- 抽象类是使用abstract声明的类。
下面的代码中, Shape类就是一个抽象类, 其中包含一个抽象方法.
1 | abstract class Shape { |
注意事项:
- 抽象类不能实例化;
- 抽象类中的抽象方法必须被子类实现,抽象类中的已经被实现方法,可以不被子类重写。
3.6. 隐式接口
Dart中没有关键字来声明接口,在默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现(因为Dart不支持多继承)
- 在开发中,我们通常将用于给别人实现的类声明为抽象类:
- 用implements实现某个类时,类中所有的方法都必须被重新实现
1 | abstract class Runner { |
3.7. Mixin混入
但是某些情况下,一个类可能希望直接复用之前类的原有实现方案,怎么做呢?
- Dart只支持单继承,那么意味着你只能复用一个类的实现。
- Dart提供了另外一种方案: Mixin混入的方式
- 除了可以通过class定义类之外,也可以通过mixin关键字来定义一个类。
- 只是通过mixin定义的类用于被其他类混入使用,通过with关键字来进行混入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24main(List<String> args) {
var superMan = SuperMain();
superMan.run();
superMan.fly();
}
mixin Runner {
run() {
print('在奔跑');
}
}
mixin Flyer {
fly() {
print('在飞翔');
}
}
// implements的方式要求必须对其中的方法进行重新实现
// class SuperMan implements Runner, Flyer {}
class SuperMain with Runner, Flyer {
}
3.8. 类的成员和方法
用static关键字来定义:
1 | main(List<String> args) { |
3.9. 枚举类型
枚举在开发中也非常常见, 枚举也是一种特殊的类, 通常用于表示固定数量的常量值。
3.9.1. 枚举
- 用enum关键字
- 仅支持字符串类型
- 枚举类型中有两个比较常见的属性:
- index: 用于表示每个枚举常量的索引, 从0开始.
- values: 包含每个枚举值的List.
1
2
3
4
5
6
7
8
9
10
11main(List<String> args) {
print(NetworkCode.success);
print(NetworkCode.success.index);
print(NetworkCode.values);
}
enum NetworkCode {
success,
failure,
redirect
}
注意事项:枚举不能子类化、混合或实现枚举。
四、泛型
如果类型只能是字符串类型可以使用继承
1 | main(List<String> args) { |
五、库的使用
Dart中任何一个dart文件都是一个库
5.1. 库的导入
语法:
1 | import '库所在的uri'; |
URI有三种不同的形式
前缀表示Dart的标准库
1
import 'dart:io';
相对路径
1
import '../test/test.dart';
Pub包管理第三方的库用前缀package
1
import 'package:flutter/material.dart';
库文件中内容的显示和隐藏
如果希望只导入库中某些内容,或者刻意隐藏库里面某些内容,可以使用show和hide关键字
- show:导入需要的函数
- hide:隐藏相关函数
1
2
3import 'lib/test/test.dart' show Sum, Mul;
import 'lib/test/test.dart' hide Sum;
当各个库有命名冲突的时候,使用as关键字来使用命名空间
1 | import 'lib/test/test.dart' as Test; |
5.2. 库的定义
library
使用library关键字给库起一个名字。
但目前我发现,库的名字并不影响导入,因为import语句用的是字符串URI
1 | library math; |
part
在之前我们使用student.dart作为演练的时候,只是将该文件作为一个库。
在开发中,如果一个库文件太大,将所有内容保存到一个文件夹是不太合理的,我们有可能希望将这个库进行拆分,这个时候就可以使用part关键字了
不过官方已经不建议使用这种方式了:
mathUtils.dart文件
1 | part of "utils.dart"; |
dateUtils.dart文件
1 | part of "utils.dart"; |
utils.dart文件
1 | part "mathUtils.dart"; |
test_libary.dart文件
1 | import "lib/utils.dart"; |
export关键字
官方不推荐使用part关键字,那如果库非常大,如何进行管理呢?
- 将每一个dart文件作为库文件,使用export关键字在某个库文件中单独导入
mathUtils.dart文件
1 | int sum(int num1, int num2) { |
dateUtils.dart文件
1 | String dateFormat(DateTime date) { |
utils.dart文件
1 | library utils; |
test_libary.dart文件
1 | import "lib/utils.dart"; |