按照惯例,先来个 Hello World 😂
void main() {
print('Hello World');
}
一、变量与常量
1.1.变量
- 没有初始化的变量默认值为
null
,基本数据类型未初始化也是null
(Dart 中甚至连 数字、方法和null
都是对象)。
int a;
double b;
String s;
Object c;
dynamic d;
var e;
print(a); // null
print(b); // null
print(s); // null
print(c); // null
print(d); // null
print(e); // null
- 可选的类型:如果变量没有明确的类型,可以使用
Object
或dynamic
关键字。
dynamic a = 5;
dynamic b = 5.6;
dynamic c = 'sss';
var d = 5;
var e = 5.6;
var f = 'sss';
- 在声明变量的时候,可以选择加上具体类型,这样可以更加清晰的表达你的意图。 IDE 编译器等工具有可以使用类型来更好的帮助你, 可以提供代码补全、提前发现 bug 等功能。
int a = 5;
double b = 4.1;
String s = 'sss';
List list = [56,3,4,6,0];
Map map = {
'key1':'value1',
'key2':'value2'
};
注意:在 Dart 中有两种表示所有对象的类型:Object 和 dynamic。但是表达的意义不同:
Object 类型说明:可以接受任意对象,只需要这个对象定义了 Object 所定义的函数 即可。
dynamic 类型说明:没有一个类型可以表达你所期望的对象。 (有可能有类型可以表达,但是你不在乎。)
如下示例:
// 接受任意对象.
void log(Object object) {
print(object.toString());
}
// 仅接受 bool 或者 String 类型, 它们不能在类型注解中表示。
bool convertToBool(arg) { // dynamic arg,默认 dynamic 类型,可省略
if (arg is bool) return arg;
if (arg is String) return arg == 'true';
throw new ArgumentError('Cannot convert $arg to a bool.');
}
1.2.final and const
- 如果以后不打算修改一个变量,可以使用
final
或者const
,被final
或者const
修饰的变量在第一次声明时必须初始化,否则编译器报错。
// 报错:The final variable 's1' must be initialized.
final String s1;
// 报错:The const variable 's2' must be initialized.
const String s2;
- 被
final
或者const
修饰的变量、对象实例一旦初始化,就不能再次赋值,否则编译器报错。
final String s1 = 'ss';
// 报错:s1,a final variable ,can only be set once.
s1 = 'bb';
const String s2 = 'ss';
// 报错:Constant variables cant't be assigned a value.
s2 = 'ff';
- 实例变量可以被
final
修饰但是不能被const
修饰,否则编译器报错。
class Person {
}
class Son {
final Person p1 = Person();
// 报错:Only static field can be declared as const.
const Person p2 = Person();
}
-
final
或者const
不能和var
同时使用,否则编译器报错。
// 报错:Members can't be declared to be both 'const' and 'var'
const var String s1 = 'Tim';
// 报错:Members can't be declared to be both 'final' and 'var'
final var String s2 = 'Tim';
- 被
final
或者const
修饰的变量,变量类型可以省略,建议指定数据类型。
const a = 'Tim';
const b = 4;
const c = 4.5;
final d = 'Tim';
final e = 4;
final f = 4.5;
-
const
变量为编译时常量。 如果const
变量在类中,请定义为static const
。 可以直接定义const
和其值,也 可以定义一个const
变量使用其他const
变量的值来初始化其值。
class Son {
static const String name1 = 'Tom';
// 报错: Only static fields can be declared as const
const String name4 = 'Tom';
void test() {
const String name2 = 'Tom';
const bar = 1000000;
const atm = 1.01325 * bar;
// 报错:Cant't have modifier 'static' here.
static const String name3 = 'Tom';
}
}
-
const
关键字不仅仅只用来定义常量。 有可以用来创建不变的值, 还能定义构造函数为const
类型的,这种类型 的构造函数创建的对象是不可改变的。任何变量都可以有一个不变的值。 但是const
常量必须用const类型的值初始化,否则编译器报错。
void test() {
var foo = const []; // foo is currently an EIA.
final bar = const []; // bar will always be an EIA.
const baz = const []; // baz is a compile-time constant EIA.
const bar1 = 1000000;
final atm1 = 1.01325 * bar1;
final bar2 = 1000000;
// 报错:Constant variables must be initialized with a constant value.
const atm2 = 1.01325 * bar2;
}
- 一个
const
变量是编译时常量。 (Const
变量同时也是final
变量。) 顶级的final
变量或者类中的final
变量在 第一次使用的时候初始化。
二、num
Dart 支持两种类型的数字:
-
int
整数值,其取值通常位于 -253 和 253 之间。 -
double
64-bit (双精度) 浮点数,符合 IEEE 754 标准。
int
和 double
都是 num
的子类。 num
类型定义了基本的操作符,例如 +
, -
, /
, 和*
, 还定义了 abs()
、ceil()
、和 floor()
等 函数。 (位操作符,例如>>
定义在 int
类中。) 如果num
或者其子类型不满足你的要求,请参考 dart:math 库,这些都是各种语言相同的套路,不多说。
var x = 1;
var hex = 0xDEADBEEF;
var bigInt = 9223372036854775807;
var y = 1.1;
var exponents = 1.42e5;
int x2 = 2;
double x3 = 6;
print('x = $x'); // x = 1
print(hex); // 3735928559
print(bigInt); // 9223372036854775807
print(y); // 1.1
print(exponents); // 142000.0
print(x2); // 2
print(x3); // 6.0
三、bool
Dart 是强
bool
类型检查,只有bool
类型的值是true
才被认为是true
,否则false
。这点和某些语言不一样, 像 1、 "aString"、 以及 someObject 等值都被认为是 false(应该是Dart version 2.0之前的版本)。在Dart version 2.0以后版本(本文Dart version 2.1.2),如果数据类型不是
bool
类型值却进行了条件判断,Dart Analysis则会报错。boo
类型的值是true
和false
都是编译时常量。assert
是Dart语言内置的断言函数,仅在检查模式下有效
在开发过程中,除非条件为真,否则会引发异常。(断言失败则程序立刻终止)。
四、String
- Dart 字符串是 UTF-16 编码的字符序列。 可以使用单引号或者双引号来创建字符串。
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
// 单引号中有单引号需要加转义字符
var s3 = 'It\'s easy to escape the string delimiter.';
/// 双引号中有单引号不需要加转义字符
var s4 = "It's even easier to use the other delimiter.";
// 单引号中使用双引号
var s5 = '分为可"区分"情况分';
- 使用三个单引号或者双引号也可以创建多行字符串对象:
var s6 = '''
You can create
multi-line strings like this one.
''';
var s7 = """This is also a
multi-line string.""";
- 可以在字符串中使用表达式,规则:
${expression}
。如果表达式是一个对象(在Dart中变量也是对象),可以省略{}
, Dart 会调用对象的toString()
函数来获取一个字符串。若表达式不是对象表达式不可省略!
var s8 = 'hello';
var s9 = '$s8 world';
print(s9); // hello world
var s10 = '${s8.toUpperCase()} WORLD';
print(s10); // HELLO WORLD
// {} 不可省略
var s11 = '$s8.toUpperCase() WORLD';
print(s11); // hello.toUpperCase() WORLD
- 一个带有r 前缀可以创建一个 “原始 raw” 字符串:
var s12 = r"In a raw string, even \n isn't special.";
print(s12); // In a raw string, even \n isn't special.
- 字符串字面量是编译时常量, 带有字符串插值的字符串定义,若干插值表达式引用的为编译时常量则其结果也是编译时常量。
// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];
const validConstString1 = '$aConstNum $aConstBool $aConstString';
print(validConstString1); // 0 true a constant string
// 报错:Const variables must be initialized with a constant value.
const validConstString2 = '$aNum $aBool $aString $aConstList';
var validConstString3 = '$aNum $aBool $aString $aConstList';
print(validConstString3);// 0 true a string [1, 2, 3]
- 字符串的拼接
String s1 = 'hello';
String s2 = 'world';
// 官方不推荐
String s3 = s1 + s2;
// 官方推荐
String s4 = '$s1$s2';
print(s3); // helloworld
print(s4); // helloworld
五、List
array (或者有序集合)是所有编程语言中最常见的集合类型。 在 Dart 中数组就是 List 对象。所以 通常我们都称之为 lists。Dart list 字面量和 JavaScript 的数组字面量类似。
lis
t里面的元素必须保持类型一致,不一致就会报错。list的角标从0开始
list
里面有多个相同的元素“X”, 调用list
对象的remove()
方法只会删除集合中第一个该元素。在java中必须遍历才能打印
list
,直接打印是地址值。Dart语言可以直接打印list
包括list
的元素。在
list
字面量之前添加const
关键字,可以 定义一个不变的list
对象(编译时常量)。
//创建一个int类型的list
List list = [10, 7, 23];
// 输出[10, 7, 23]
print(list);
// 使用List的构造函数,也可以添加int参数,表示List固定长度,不能进行添加 删除操作
var fruits = new List();
// 添加元素
fruits.add('apples');
// 添加多个元素
fruits.addAll(['oranges', 'bananas']);
List subFruits = ['apples', 'oranges', 'banans'];
// 添加多个元素
fruits.addAll(subFruits);
// 输出: [apples, oranges, bananas, apples, oranges, banans]
print(fruits);
/*
获取List的长度,判断List是否为空时最好不要用lenth属性,
用isEmpty或者isNotEmpty,可以提高性能
*/
print(fruits.length);
// 判断List是否为空
bool isEmpty = fruits.isEmpty;
bool isNotEmpty = fruits.isNotEmpty;
// 获取第一个元素
print(fruits.first);
// 获取元素最后一个元素
print(fruits.last);
// 利用索引获取元素
print(fruits[0]);
// 查找某个元素的索引号
print(fruits.indexOf('apples'));
// 删除指定位置的元素,返回删除的元素
print(fruits.removeAt(0));
// 删除指定元素,成功返回true,失败返回false
// 如果集合里面有多个“apples”, 只会删除集合中第一个该元素
fruits.remove('apples');
// 删除最后一个元素,返回删除的元素
fruits.removeLast();
// 删除指定范围(索引)元素,含头不含尾
fruits.removeRange(start,end);
// 删除指定条件的元素(这里是元素长度大于6)
fruits.removeWhere((item) => item.length >6);
// 删除所有的元素
fruits.clear();
/*
在 `list` 字面量之前添加 `const` 关键字,
可以 定义一个不变的 `list` 对象(编译时常量)
*/
var constantList = const [1, 2, 3];
- 排序。
排序一个 list 可以使用 sort()
函数。还可以提供一个用来排序 的比较方法。排序方法返回值 为:对于小的值 为 < 0;对于相同的值为 0 ;对于大的值为 > 0。 下面的示例使用由 Comparable 定义的 compareTo()
函数,该函数也被 String 实现了。
var fruits = ['bananas', 'apples', 'oranges'];
// Sort a list.
/**
* Sorts this list according to the order specified by the [compare] function.
*
* The [compare] function must act as a [Comparator].
*
* List<String> numbers = ['two', 'three', 'four'];
* // Sort from shortest to longest.
* numbers.sort((a, b) => a.length.compareTo(b.length));
* print(numbers); // [two, four, three]
*
* The default List implementations use [Comparable.compare] if
* [compare] is omitted.
*
* List<int> nums = [13, 2, -11];
* nums.sort();
* print(nums); // [-11, 2, 13]
*
* A [Comparator] may compare objects as equal (return zero), even if they
* are distinct objects.
* The sort function is not guaranteed to be stable, so distinct objects
* that compare as equal may occur in any order in the result:
*
* List<String> numbers = ['one', 'two', 'three', 'four'];
* numbers.sort((a, b) => a.length.compareTo(b.length));
* print(numbers); // [one, two, four, three] OR [two, one, four, three]
*/
fruits.sort((a, b) => a.compareTo(b));
assert(fruits[0] == 'apples');
六、Set
Dart 中的 Set
是一个无序集合,里面不能保护重复的数据。 由于是无序的,所以无法通过索引来从 set 中获取数据:
var ingredients = new Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
assert(ingredients.length == 3);
// Adding a duplicate item has no effect.
ingredients.add('gold');
assert(ingredients.length == 3);
// Remove an item from a set.
ingredients.remove('gold');
assert(ingredients.length == 2);
- 使用
contains()
和containsAll()
来判断 set 中是否包含 一个或者多个对象:
var ingredients = new Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Check whether an item is in the set.
assert(ingredients.contains('titanium'));
// Check whether all the items are in the set.
assert(ingredients.containsAll(['titanium', 'xenon']));
- 交集是两个 set 中都有的数据的子集:
var ingredients = new Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Create the intersection of two sets.
var nobleGases = new Set.from(['xenon', 'argon']);
// 拿到交集的值(Set类型)
var intersection = ingredients.intersection(nobleGases);
assert(intersection.length == 1);
assert(intersection.contains('xenon'));
七、Map
通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。
Map中的键是唯一的,相同Key的值会覆盖之前的数据。
值可以为空字符串或者
null
所查找的键不存在,则返回
null
。使用
.length
来获取Map
中键值对的数目。可使用
const
可以创建一个编译时常量的Map
。
// 声明并赋值
var gifts = {
// Keys Values
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
// Keys Values
2: 'helium',
10: 'neon',
18: 'argon',
};
// 先声明,后赋值
Map map1 = new Map();
map1['first'] = 'value1';
map1['second'] = 'value2';
map1['third'] = 'value3';
var map2 = new Map();
map2[2] = 'helium';
map2[10] = 'neon';
map2[18] = 'argon';
// 指定键值对的参数类型
var aMap = new Map<int, String>();
// Map的赋值,中括号中是Key,这里可不是数组
aMap[1] = '小米';
/*
Map中的键是唯一的,相同Key的值会覆盖之前的数据
*/
aMap[1] = 'alibaba';
// map里面的value可以相同
aMap[2] = 'alibaba';
// map里面value可以为空字符串
aMap[3] = '';
// map里面的value可以为null
aMap[4] = null;
print(aMap);
// 检索Map是否含有某Key
assert(aMap.containsKey(1));
/*
获取键值对个数,判断Map是否为空时最好不要用lenth属性,
用isEmpty或者isNotEmpty,可以提高性能
*/
print(aMap.length);
// 判断List是否为空
bool isEmpty = aMap.isEmpty;
bool isNotEmpty = aMap.isNotEmpty;
//删除某个键值对
aMap.remove(1);
print(aMap);
// 可使用const 可以创建一个编译时常量的 Map。
final constantMap = const {
2: 'helium',
10: 'neon',
18: 'argon',
};
八、Runes
在 Dart 中,runes 代表字符串的 UTF-32 code points。
Unicode 为每一个字符、标点符号、表情符号等都定义了 一个唯一的数值。 由于 Dart 字符串是 UTF-16 code units 字符序列, 所以在字符串中表达 32-bit Unicode 值就需要 新的语法了。
通常使用 \uXXXX
的方式来表示 Unicode code point, 这里的 XXXX 是4个 16 进制的数。 例如,心形符号 (♥) 是 \u2665
。 对于非 4 个数值的情况, 把编码值放到大括号中即可。 例如,笑脸 emoji (😆) 是 \u{1f600}
。
String 类 有一些属性可以提取 rune
信息。 codeUnitAt
和 codeUnit
属性返回 16-bit code units。 使用 runes
属性来获取字符串的runes
信息。
var clapping = '\u{1f44f}';
print(clapping); // 👏
print(clapping.codeUnits); // [55357, 56399]
print(clapping.runes.toList()); // [128079]
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input)); // ♥ 😅 😎 👻 🖖 👍
九、Symbol
Symbol
字面量定义是编译时常量,一个 Symbol object 代表 Dart 程序中声明的操作符或者标识符。 你也许从来不会用到 Symbol,但是该功能对于通过名字来引用标识符的情况 是非常有价值的,特别是混淆后的代码, 标识符的名字被混淆了,但是 Symbol 的名字不会改变。关于 symbols 的详情,请参考 dart:mirrors - reflection。
使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 #
符号:
#radix
#bar