# 变量和类型
# 变量
# 创建一个变量
因为在Dart语言中,一切皆对象,一个字符串,一个数字甚至null都是一个对象.因此,变量存储的是对象的引用.
var s = 'Hello Dart';
print(s); // Hello Dart
虽然Dart是一门强类型的语言,但它具有类型推导的功能,即我们在定义的时候,不一定非要强制定义变量的具体类型.像上面的代码中,我们使用var
来声明一个变量,Dart会自行帮我们判断出这是一个String类型的变量.上面的代码我们也可以直接指定一个具体的类型:
String s = 'Hello Dart';
print(s);
但我们再次给它赋值其他类型的时候,是会报错的.我们在上面的代码后面添加上如下代码,再次运行:
s = 23;
print(s);
会出现Error: A value of type 'int' can't be assigned to a variable of type 'String'.
的错误提示.它告诉我们,不能将一个int整型赋值给一个类型为String的变量.但是下面的场景中,类型是可以改变的:
var s;
print(s); // null
s = 'Hello Dart';
print(s); // Hello Dart
s = 12;
print(s); // 12
当我们用var声明一个变量,但是不给它初始化,后续的赋值操作中,可以是多种类型的.并且,我们可以看到,未被初始化的变量,它的默认值为null. 一般我们建议,在声明的时候指定一个具体的类型.如果暂时还不知道类型的话,可以使用对象类型和动态类型.
# 对象类型 Object
Object s = 'Hello Dart';
print(s); // Hello Dart
s = 12;
print(s); // 2
# 动态类型 dynamic
dynamic s = 'Hello Dart';
print(s); // Hello Dart
s = 12;
print(s); // 2
这两者虽然都是可以让变量接收不同的类型,但是它们是有本质的区别的.因为在Dart中,一切皆对象,所有的类型都是继承自Object,所以对象类型可以接收不同的类型.而dynamic则是让代码在编译阶段不执行类型检查.
# 创建一个常量
通常,创建一个常量,我们使用const
或final
关键字,而不是var或者其他类型.
# const
const n = 1;
n = 2;
编译器会报错:Error: Can't assign to the const variable 'n'.
.告诉我们无法分配给一个常量
# final
final n = 1;
n = 2;
编译器会报同样的错误.
那么这两者有什么区别呢?
const
是编译时常量,即它的值在编译时就已经固定了,而final
则在第一次使用时被初始化(当然,const
算是隐式final
类型).所以实例变量可以是final
类型但不能是const
类型,且必须在构造函数体执行之前初始化final
实例变量.
# 类型
Dart语言支持多种内建类型
- Number
- String
- Booleab
- List
- Set
- Map
- Rune
# Number
有int
和double
这两种类型,它们都是num
的亚类型.其中 int
代表整数, double
(也是采用 IEEE 754 标准)代表浮点数.num
拥有各种基本运算方法: +
, -
, *
, /
,~/
, abs()
, ceil()
, floor()
等.
# int
声明一个整数
var a = 1
int b = 1;
# double
声明一个浮点数
var a = 1.1;
int b = 1.1;
# 运算
/
除法运算, %
除法取余, ~/
除法取整
int a = 7;
int b = 2;
print(a / b); // 3.5
print(a % b); // 1
print(a ~/ b); // 3
# String
可以采用单引号'
, 双引号 "
, 三引号 '''
来创建字符串,其中三引号实现多行字符串对象的创建.
String a = 'Hello Dart';
String b = "Hello Dart";
String c = 'Hello I\'m Dart';
String d = '''
Hello World
I'm Dart
''';
字符串通过 ${expression}
内嵌表达式,如果表达式是一个标识符,则{}可以省略
String language = 'Dart';
print('Hello ${language}'); // Hello Dart
print('Hello $language'); // Hello Dart
使用r前缀,创建原始raw字符串
String s = r'Hello \n world';
print(s); // Hello \n world
# 类型转换
字符串与数字之间的类型转换
// String => int
String s = '1';
int a = int.parse(s);
assert(a == 1);
// String => double
String s2 = '1.2';
double b = double.parse(s2);
assert(b == 1.2);
// int => String
int n = 1;
String c = n.toString();
assert(c == '1');
// double => String
double m = 1.2345;
String d = m.toStringAsFixed(2);
assert(d == '1.23');
其中assert
是断言,在生产环境会被忽略,在开发环境下assert(condition)
会在非true的情况下抛出异常.
# Boolean
使用 bool
类型表示布尔值,有字面量 true
和false
,都是编译时常量.Dart的类型安全意味着不能使用if(nonbooleanValue)
或 assert(nonbooleanValue)
,而应该明确检查.
String s = '';
assert(s.isEmpty);
int n = 0;
assert(n == 0);
var o = null;
assert(o == null);
var t = 0 / 0;
assert(t.isNaN);
在JavaScript中,因为存在隐式转换,所以我们可以有如下操作
let arr = []
if(arr){
console.log('1')
}else{
console.log('2')
}
结果会输出1
,但是在Dart里面这样就是行不通的,if(flag)
括号里面的flag必须是个很明确的bool值.
# List
就是Dart中的数组类型,用List
来定义.
List list = [1,2];
print(list); // [1, 2]
list.add(3);
print(list); // [1, 2, 3]
list.addAll([4,5]);
print(list); // [1, 2, 3, 4, 5]
print(list.length); // 5
list[2] = 22;
print(list); // [1, 2, 22, 4, 5]
也可以约束List中的每一个元素的类型
List<int> list = [1,2];
list.add('2'); //Error: The argument type 'String' can't be assigned to the parameter type 'int'.
这里我们尝试将一个错误类型的值添加进去,则会提示错误.但其实如果我们将代码改成如下:
var list = [1,2];
list.add('2');
也还是会报同样的错误提示.这是因为我们之前讲到过的,有默认的类型推导,判断出list的类型为LIst<int>
,而现在添加的却是一个String类型的字符串.
# Set
是一个元素唯一的无序集合
var set = {'zhangsan','lisi'}; // 创建一个set
var set = {'zhangsan','lisi'};
这里会有个默认的类型推导,判断出set的类型为 Set<String>
,如果尝试将错误类型添加进去,则会提示错误.
要创建一个空集,使用前面带有类型参数的{} 或将{}赋值给Set类型的变量
var set = <String>{};
Set<String> set2 = {};
var o = {}; //创建的是一个Map,而不是一个Set
使用add()
和addAll()
添加元素,使用 .length
获取Set中元素的个数
var set = <String>{};
var set2 = {'list'};
set.add('zhangsan');
set.addAll(set2);
print(set); // {zhangsan, list}
print(set.length); // 2
在Set字面量之前添加const,可以创建一个编译时Set常量,此后便不再可以更改set的内容.
final set = const {
'zhangsan'
};
# Map
关联keys和values的对象,keys和values可以是任何类型的对象
var map = {
'name':'zhangsan',
'location':'China'
};
这里会有个默认的类型推导,判断出map的类型为 Map<String,String>
,如果尝试将错误类型添加进去,则会提示错误.
使用Map构造函数创建,Dart一切皆是对象(都是类的实例),所以变量可以使用构造函数进行初始化
var map = Map();
map['name'] = 'zhangsan';
map['age'] = 18;
print(map); // {name: zhangsan, age: 18}
在获取values值时,如不存在的返回null:
print(map['name']);
print(map['gender']); // null
使用 .length
获取键值对数量
print(map.length); // 2
# Rune
用来表示字符串中的UTF-32编码字符.由于Dart字符串是UTF-16编码单元,因此要在字符串中表示32位的Unicode需要特殊语法支持: \uXXXX
用4个16进制数来表示,对于非4个数值的情况,用大括号把编码值括起来即可.
print('\u2665'); // ♥
print('\u{1f44d}'); // 👍
Runes input = new Runes('\u2665 \u{1f47b} \u{1f44d}');
print(new String.fromCharCodes(input)); // ♥ 👻 👍