[Flutter]足够入门的Dart语言系列之变量的类型:bool、String、num、List、Set和Map

1,744 阅读8分钟

我正在参加「掘金·启航计划」

变量的类型指的是变量的特性或特征,比如表示数字类型、文本类型、集合类型等,表示的是一类数据。

Dart提供以下的内置类型:

  • 数字:int, double (整型(表示整数),浮点型(表示小数))
  • 布尔:bool (true/false)
  • 字符串:String
  • 列表:List (也被称为 arrays 数组)
  • 集合:Set
  • 映射:Map
  • Null:null

Dart 中最基础类型只有 boolnum ,表示真假数字。其他类型为聚合类型。null属于特殊类型,表示空,它唯一一个不属于Object的类型。

此外,数据类型还有Runes与Symbols。

数字类型

整型与浮点型

int count = 49; // 整型
double pi = 3.14; // 浮点型

intdouble 都是 num 的子类。因此也可以用num表示数字,但是变量的类型会在编译执行后,自动转换为特定的int类型或double类型。

runtimeType属性可以查看一个变量的运行时类型。

查看num运行时类型:

num a = 57; // 整型
num b = 3.28; // 浮点型
print("a:${a.runtimeType}");
print("b:${b.runtimeType}");

/* 输出 */
a:int
b:double

num是一个抽象类。抽象类没有实现,不能实例化,任何抽象类,在运行时都是具体的实现类型。

数字的方法

  • num常用的方法
num b = 3.28;
b.abs(); 		    // 绝对值 : 3.28
b.ceil(); 		    // 向上取整: 4
b.floor(); 		    // 向下取整: 3
b.round(); 		    // 四舍五入取整: 3
b.truncate();		// 去除小数部位取整: 3
String str = b.toString();       // 转换为字符串: 3.28
String v = b.toStringAsFixed(1); // 四舍五入,保留几位小数,返回字符串: 3.3
  • 解析为数字

doubleintparse方法用于解析数据获取数字;tryParse方法用于解析获取数字,如果无法转换为对应数字,将会返回null。

double result1 = double.parse('3.3');
int result2 = int.parse('10');

double? result3 = double.tryParse('a3');
int? result4 = int.tryParse('10.2');
  • 进制转换

int类型提供了进制转换方法toRadixString,可以把整型转化为指定的进制,以字符串形式输出。

如下将变量转换为2进制、16进制字符串:

int a = 2022;
print(a.toRadixString(2)); // 11111100110
print(a.toRadixString(16));// 7e6

parse/tryParse方法将其他进制的字符串转换为int类型。

int? int1 = int.tryParse('7e6',radix: 16);
print(int1); // 2022

int? int2 = int.tryParse('11111100110',radix: 2);
print(int2); // 2022

布尔bool

布尔类型表示真假。只有两个取值:

bool a=true;
bool b=false;

字符串

字符串类型是使用最广泛的类型,表示文本形式的数据,任何数据或信息都可以通过文本表示出来。

字符串表示若干个字符组成的对象。Dart字符串使用的是UTF-16编码的字符序列。

字符串定义

Dart中字符串支持单引号双引号三单引号三双引号形式的字面量。

String a = 'hello, 单引号';
String b = "hello, 双引号";
String c = '''hello, 三单引号''';
String d = """hello, 三双引号""";

单引号和双引号没有任何区别,根据习惯使用即可(偏推荐单引号)。唯一的一个区别是对字符串内引号的转义。

String a = 'hello, \'单引号中转义单引号\'';
String b = "hello, '双引号中直接使用单引号'";

String c = 'hello, "单引号中直接使用双引号"';
String d = "hello, \"双引号中转义双引号\"";

三引号 可以表示多行字符串。

String e = '''
你好,我是‘
三个单引号
"组成的"多行
字符‘串
''';

r前缀表示原始字符串

字符串前加上 r 前缀创建原始(raw)字符串(即不会被做任何处理(比如转义)的字符串)。

var s = r'在 raw 字符串中,转义字符串 \n 会直接输出 “\n” 而不是转义为换行。';

主要是保证输入的内容保持不变,原样输出。

模板字符串(字符串插值)

通常可以通过+号实现字符串的拼接。

比如多个字符串变量:

String str1 = '我们';
String str2 = '工作';
String str3 = '更好的人';
int n=0;

String result = str1 + '努力' + str2 + ',成为' + str3 + '。只有我失败了...' + (n+2).toString();

print(result); // 我们努力工作,成为更好的人。只有我失败了...2

当变量很多时,+号就非常不简洁、麻烦。

Dart提供了字符串插值的方式,在字符串中通过 ${变量} 插入变量,方便拼接。{}中还可以进行简单的计算。如果 {} 中只有变量名称,可以省略 {}

比如下面,{n+3+3}内执行运算时不可省略。

String result2 = '${str1}努力$str2,成为$str3。只有我失败了...${n+3+3}';

print(result2); // 我们努力工作,成为更好的人。只有我失败了...6

string interpolation 字符串插值。 这种字符串也会叫做模板字符串。组成字符串的形式叫字符串模板。

字符串方法

  • 索引获取字符

字符串是一组字符组成的聚合对象,可以通过[]访问第几个字符,字符位置也叫索引,索引下标从0开始。

比如str[5]表示索引6的字符,顺序为第5个。

  • length属性可以获取字符长度。
String str = '我是一个中文字符串';

print(str[5]);            // 文
print(str[str.length-1]); // 串
  • substring截取字符串

substring方法用于截取字符串。需要传入两个参数:分别表示 起始索引结束索引,返回的子字符串包含起始字符,不包含结束字符。第二个参数结束索引可省略,表示一直截取到字符结尾。

String str = '我是一个中文字符串';

print(str.substring(4));    // 中文字符串
print(str.substring(4,7));  // 中文字
  • trim去除头尾空白符号;trimLeft只去除开头的空白符;trimRight只去除末尾的空白符:
String str = '  我是一个中文字符串     ';

print(str.trim());
print(str.trimLeft());  
print(str.trimRight());
  • toUpperCase将字符全部转为大写字母;toLowerCase将字符全部转为小写字母。

  • startsWith是否以指定字符串开头;endsWith是否以指定字符串结尾;contains是否包含指定字符串。返回bool类型(true/false)。

  • indexOf返回首次出现指定字符串的索引,不存在则返回-1

  • replaceAll替换字符串;

  • split将字符串拆分为列表List;

  • padLeft字符串左补齐指定宽度的指定字符,默认补充空格;padRight字符串右补齐。

多数String相关的方法都支持正则表达式,因此可以实现更多、更灵活的字符串处理。

List列表

List对象表示多个元素组成的序列,列表用 [] 进行定义,其内通过 逗号, 来分隔元素。在其他语言中通常叫做组数(Array),是最常见的集合类型。

如下,使用 列表字面量(list literals)创建一个繁体0~9的列表:

List<String> cnBigNumUnits = [
  '零','壹','贰','叁','肆','伍','陆', '柒','捌','玖',
];

通过索引就可以访问某个元素。【索引从0开始】

print(cnBigNumUnits[3]);

一些常用属性:

  • length 长度

  • reversed 翻转List

  • isEmpty 是否为空

  • isNotEmpty 是否不为空

const关键字创建一个编译时常量

var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.

扩展操作符(...)和 空感知扩展操作符(...?)

Dart 2.3 引入。

spread operator扩展操作符 ...null-aware spread operator空感知扩展操作符 ...? 可以简介的将多个值插入到集合。

var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);

如果扩展操作符右边可能为null,使用空感知扩展操作符(...?)可以避免产生异常

var list=null;
// ...
var list2 = [0, ...?list];
assert(list2.length == 1);

创建List时使用if或for

如下,使用collection if创建List,可能包含3个或4个元素。

var nav = ['主页', '发现', '阅读', if (promoActive) '个人中心'];

使用collection for操作列表中的元素之后添加到List中:

var ints = [1, 2, 3];
var strings = ['#0', for (var i in ints) '#$i'];

assert(strings[1] == '#1');
print(strings);     // [#0, #1, #2, #3]

关于List泛型列表的具体类型【不推荐使用List】

可以看到上面定义cnBigNumUnits时,类表类型为List<String>,它是List的泛型表示,也就是实际类型是List<String>

<>内是一个类型参数,通常传递类型,表示该List是泛型的一个具体类型的List。

使用类型推断,以及runtimeType属性查看创建数组的具体类型:

var myList=[1,'2',3,'四'];

print(cn); // [1, 2, 3, 四]
print(cn.runtimeType); // JSArray<Object>
List myList2=[1,'2',3,'四'];
print(myList2);
print(myList2.runtimeType);  // JSArray<dynamic>

print('---------------');

List myList3=[1,2,3];
print(myList3);
print(myList3.runtimeType);  // JSArray<dynamic>

print('---------------');

var myList4=[1,2,3];
print(myList4);
print(myList4.runtimeType); // JSArray<int>

print('---------------');

var myList5=['一','二','三'];
print(myList5);
print(myList5.runtimeType); // JSArray<String>

可以看到,直接使用List的类型为JSArray<dynamic>,动态类型总是不推荐的。因此实际中,最好使用具体的泛型,如List<String>List<int>等,使用类型推断var,后面的列表也要尽量类型一致。

类表常见方法

修改元素值

[idx]通过索引访问元素,使用=赋值就可以修改元素

cnBigNumUnits[6] = '六';
print(cnBigNumUnits);

cnBigNumUnits[6] = '陆';
print(cnBigNumUnits);

添加元素

  • addaddAll 方法用于在列表末尾添加元素;insertinsertAll 方法在指定索引处添加元素。

addAllinsertAll的作用更像是拼接数组。

List<String> cnBigNumUnits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
cnBigNumUnits.add('拾');
cnBigNumUnits.add('佰');
cnBigNumUnits.addAll(['仟', '萬', '亿']);
cnBigNumUnits.insert(2, '点');
cnBigNumUnits.insertAll(2, ['横', '撇']);

print(cnBigNumUnits);

删除元素

  • removeAt(idx):删除 指定索引 的元素
  • remove(item):删除 某个元素值
  • removeLast():移除并返回最后一个元素
  • removeRange(int start, int end):移除范围内的元素
cnBigNumUnits.removeAt(2);
cnBigNumUnits.removeAt(2);
cnBigNumUnits.remove('点');
print(cnBigNumUnits);

join() List转换成字符串

List<String> cnBigNumUnits = [
  '零','壹','贰','叁','肆','伍','陆', '柒','捌','玖',
];
  
print(cnBigNumUnits.join());    // 零壹贰叁肆伍陆柒捌玖
print(cnBigNumUnits.join(',')); // 零,壹,贰,叁,肆,伍,陆,柒,捌,玖

Set 集合

集合的使用和增删

通过大括号{}定义一个集合,集合中元素用,分割。

Set和数学中集合的概念一样,可容纳若干个元素,无序且不包含相同元素。Set中没有索引的概念。

Set<String> cnBigNumUnits = {'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'};

Set<String> cnBigNumUnits = {}var cnBigNumUnits = <String>{} 都可以创建空的集合。

add添加元素到集合中,重复重复将不被添加进去。

remove移除指定的元素。

addAllremoveAll 用来添加和移除多个元素。

cnBigNumUnits.addAll({'零', '元','角','分'});
cnBigNumUnits.addAll(['拾', '佰', '仟', '萬', '亿']);
cnBigNumUnits.removeAll({'元','角','分'});

集合运算

集合间的运算关系主要有交集(intersection)、并集(union) 、补集 (difference)。

Set<String> cnBigNumUnits = {'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'};
Set<String> part = {'零', '壹', '贰', '元', '角', '分'};

cnBigNumUnits.intersection(part); // 交集
// {零, 壹, 贰}

cnBigNumUnits.union(part); // 并集
// {零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖, 元, 角, 分}

cnBigNumUnits.difference(part); // 补集
// {叁, 肆, 伍, 陆, 柒, 捌, 玖}

Map 映射

Map是用来关联key和value的对象,其中的键和值可以是任何类型的对象。每个 键 只能出现一次但是 值 可以重复出现多次。

在其他编程语言中,Map映射也被称为字典类型(Dictionary),或哈希类型(hash)。

映射通过 {} 进行定义,其中包含若干个 key : vlaue 的键值对。下面使用 Map 字面量创建映射:

Map<String,String> dict = {
  'about': '关于',
  'boot': '启动',
  'card': '卡片',
};

还可以使用Map构造器创建Map:

var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

Map通过[key]来访问,如果key不存在,将会返回null;通过赋值=可以修改某个key的value,如果key不存在则会添加。

remove(key)用于移除元素。

// 访问
print(dict['card']); // 卡片

// 修改
dict['boot'] = '启动,靴子';

// 增加
dict['dog'] = '狗';
dict['cat'] = '猫';

//通过 key 删除元素
dict.remove('cat');

.length返回Map键值对的数量。

Map 可以像 List 一样支持使用扩展操作符(... 和 ...?),以及集合的 if 和 for 操作。

List、Set、Map之间的关系和相互转换

  • List 和 Set 可以相互转化

Set中的元素是不重复的,如果 List 中有重复的元素,通过 toSet 方法转化为 Set 可以去除重复元素,再把 Set 通过 toList 转化为 List ,可以实现 去重 的需求。

List<String> cnNumUnits = ['零', '壹', '贰', '叁','贰', '贰'];
Set<String> cnNumSet = cnNumUnits.toSet();
print(cnNumSet); // {零, 壹, 贰, 叁}

List<String> cnNumUnique  = cnNumSet.toList();
print(cnNumUnique); // [零, 壹, 贰, 叁]
  • List 通过 asMap 方法可以返回一个 Map 对象。其中键是索引,值是元素值
List<String> cnNumUnits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
Map<int,String> cnNumMap = cnNumUnits.asMap();

print(cnNumMap);
// {0: 零, 1: 壹, 2: 贰, 3: 叁, 4: 肆, 5: 伍, 6: 陆, 7: 柒, 8: 捌, 9: 玖}
  • 通过 Map.fromIterables 方法,可以根据两个可迭代对象创建映射对象,前者是 key ,后者是 value 。(ListSet 都是可迭代对象)
List<String> cnNumUnits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖','拾','佰','仟','萬'];
Set<int> numUnitsSet = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,100,1000,10000};
Map<int,String> map = Map.fromIterables(numUnitsSet,cnNumUnits);

print(map);
// {0: 零, 1: 壹, 2: 贰, 3: 叁, 4: 肆, 5: 伍, 6: 陆, 7: 柒, 8: 捌, 9: 玖, 10: 拾, 100: 佰, 1000: 仟, 10000: 萬}
  • Map 对象可以通过 keysvalues 获取可迭代对象,再通过 toListtoSet 获得 ListSet 对象。
Map<String,String> dict = {'about': '关于', 'boot': '启动', 'card': '卡片'};
dict.keys.toList();
dict.values.toList();

dict.keys.toSet();
dict.values.toSet();

可空数据类型?

Dart中,通过在类型后面添加?符号,表示该类型可为空。一个不可为空类型是不能赋值为null(通常也不需要设置为null),如果有必要使用null值,则必须通过?将类型变为可空类型。

可空类型:

int? a=null;
String? s=null;

参考