1.本篇简介
这一篇我们主要介绍Dart中变量声明的关键字,总共有以下几个关键字:var,final,const,Object,dynamic。下面我们会通过示例来介绍每个关键字的使用和特点。
2.var
2.1 示例
我们先通过以下代码,来看看var是怎么使用的:
```
var a =1;// ==> 等价与 int a = 1;
print(a);//1
print(a is int);//true is 关键字用来判断 变量是否某类型,此段代码中 判断a是否为int类型
var b = "ss";
print(b);//ss
print(b is int);//false
```
上面代码可以看出,var能用于声明任何一种的变量类型,但是,根据后面print函数打印的结果(is关键字处),dart 好像能推断出变量的实际类型,我们接着往下看。
```
var a = 1;
a = "ss";//报错:A value of type 'String' can't be assigned to a variable of type 'int'.
//类型'String'的值不能赋给类型'int'的变量
```
明显,dart给我们报错了,String类型的值不能赋给int类型的变量(在写出代码时,as 已经标红)。
上面的代码等价于java中:
``
int a = 1;
a = "ss";
```
这样操作变量,明显是错的,dart本身也是一个强类型语言,var只是dart提供的一个语法糖,当var变量在进行编译的时候,会自动匹配到对应的实际类型,并用实际类型来替换该类型的声明。
2.2 总结
通过上面的示例,我们可以总结出var使用方法与需要注意的地方。
1.var 可以接受任何变量类型。
2.var声明的变量一旦赋值,类型确定,不能再改变量类型。
3.final 和 const
3.1示例
这里我们将 final 和 const 放在一起进行说明,因为这两个关键字有一定的异同点,首先我们通过代码来了解final和const的相同点。
```dart
final a;//错误 The final variable ';' must be initialized. 必须初始化final变量
const b;// 错误 同上
final a1 ="ss";// ==>等价于 final String a1 = "ss";
const b1 ="ss"; // ==> 等价于 const String b1 = "ss";
a1 = "sss";//错误 a final variable ,can only be set once; final变量只能设置一次
b1 = "sss";//错误 同上
final String a2 ="ss"; // ==> 等价于 final a2 = "ss"
const String b2 ="ss"; // ==> 等价于 const b2 = "ss";
final var a3 ="ss";//错误 Members can't be declared to be both 'final' and 'var'.Try removing the keyword 'var'.
//成员不能同时被声明为“final”和“var”。尝试删除关键字“var”。
const var b3 ="ss";//错误 同上
```
通过上面的示例可以看出,final和const的相同点有:
1.final 和 const 声明变量的时候必须初始化,而且初始化后不允许修改。
2.使用final或const修饰变量时,可以省略变量具体类型如:上面的String。
3.final或const 不能与var同用。
我们再次通过代码,来看看final和const的不同点:
```dart
// 示例1
final finalTime =DateTime.now();//获取当前时间
const constTime =DateTime.now();//Cannot invoke a non-'const' constructor where a const expression is expected. 不能在需要const表达式的地方调用非'const'构造函数。
// 示例2
final finalList = [1,2,3];
finalList[0] =3;
print(finalList);//3,2,3
const constList = [1,2,3];
constList[0] =3;
print(constList);//Cannot modify an unmodifiable list 无法修改无法修改的列表
// 示例3
final finalList1 = [1,2,3];
final finalList2 = [1,2,3];
const constList1 = [1,2,3];
const constList2 = [1,2,3];
print(identical(finalList1, finalList2));//false identical 用于判断两个变量是否指向同一个对象
print(identical(constList1, constList2));//true
```
上面代码中,分成了三个示例,每一个示例,表达了final和const的一个不同点。我们对三个示例进行分析:
-
DateTime.now()用于获取当前的时间,是DateTime的一个命名函数,final修饰的时候并没有什么问题,但是用const进行修饰的时候,报错。不能在const表达式的地方调用非“const”构造函数。我们先看final和const的定义:
final用于修饰在运行时有确定值的常量。(运行时常量,在代码运行的时候才会被赋值,且final是惰性初始化,在运行时第一次使用前才会被初始化)
const用于修饰在编译时有确定值的常量。(编译时常量就是在代码编译的时候,这个常量就被存入到引用它的类的常量池中了,与定义它的类没有什么关系,运行时引用编译时常量,并不会使定义这个常量的类被加载)
那么上面的示例1中,DateTime.now()是获取当前时间的,获取当前时间必须程序运行起来才能获取到值,那么在编译期间是不能确定值的。修改构造函数的为const的前提条件是该类中的所有成员变量都要被final或const修饰。
2.示例2定义了两个list,分别用final和const来修饰,可以看到,final修饰的list允许修改其内部的值,const修饰的list不允许修改其内部的值。表明:const的不可变性具有传递性,final的不可变性不具有传递性。
3.示例3定义了四个list,其中两个用final修饰,两个用const修饰,identical方法用于判断两个变量是否指向对象,可以看到final修饰的两个相同的list并不指向同一个对象,const修饰的两个相同的list则指向同一个对象。表明:值相同时final在内存中会重复创建,const会引用相同的值。
以上三点就是final和const的不同点。
4.Object和dynamic
这里我们同样将Object和dynamic放在一起进行说明,因为这两个也是有一定的异同点。下面我们通过代码来展示相同点。
```dart
Object o =3;
o ="object";
print(o);//object
dynamic d =3;
d ="dynamic";
print(dynamic);//dynamic
```
上面代码中,和var截然不同的是,原本是int类型的 变量o 和 变量d,在后面都被重新赋值为String类型,并且能够输出变量而不报错,表示 Object和dynamic 能接受任意类型,并且是动态的(动态任意类型)。
我们再看下面的代码:
```dart
class Person{
void speak(){
print("hello world");
}
@override
void toString(){
print("Person");
}
}
Object o = Person();
o.toString();//Person
o.speak();//The method 'speak' isn't defined for the class 'Object' ; speak方法没有为Object类定义
dynamic d = Person();
d.toString();//Person
d.speak();//hello world
```
上面代码中,定义了一个Person类,重写了toString并定义了一个speak方法,可以看到,当变量o调用toString方法时能正常打印,但当变量o调用Person中定义的speak方法时,报错,speak方法没有为Object类定义,也就是说dart认为变量o的类型为Object。而用dynamic声明的变量d 并没有报错。让我们来看看下面的代码,看看dynamic声明的变量在什么时候会报错:
```dart
//在上面的基础上 我们再定义一个类
class Machine{
void work(){
print("hong long long");
}
}
dynamic d = Person();
d.toString();//Person
d.speak();//hello world
d.work();//Class 'Person' has no instance method 'work'. 类“Person”没有实例方法“work”。
```
可以看到,当我们调用Machine类中定义的work方法时,变量d 报错了,那么此时变量d的类型就是Person。
由上面的结果可以总结出Object和dynamic的不同点:
1.检查类型的时间:
1.1 Object在编译阶段检查类型。
1.2 dynamic在运行时检查类型。
2.实例类型:
2.1 Object声明的变量,最后的类型为Object(Object为所有类的基类,多态,导致Object能够用来创建不同实例的变量),所以,Object声明的变量,只能调用在Object中定义过的方法(函数)。
2.2*** dynamic声明的变量,因为在运行时才会进行类型检查,所以只有在运行时才会成为对应的实例,(编译阶段不检查类型,所以出现了上述中 变量d 调用Machine类中的work方法并报错),和Object不同能调用Person方法的原因是:被dynamic修饰的变量编译器会提供尽可能多的组合,使得dynamic声明的变量能够调用尽可能多的方法(函数)***。
5.最后
在实际开发时,尽量少用dynamic,因为它容易引起运行时错误。
const 与 static 连用为静态常量。
类中的变量如果被const修饰,外界引用时,并不会导致这个类被加载,读者可以自己写一个类,并在类中定义一个静态代码块进行检测。
最后附上思维导图一张:
image作者本人也是刚接触dart 和 flutter,写博客的原因是因为想记录学习的脚步,同时也是为了自己更加深刻的理解。如果有不对的地方,请务必指正。也欢迎大家一起学习交流。
网友评论