变量/函数的作用域
与Kotlin一样,Dart可以在top-level进行变量或函数声明,main
函数也在top-level声明,相对于Java作用域变得更广。
Dart中万物皆对象,所有的函数本质上都是Function
类型的实例变量,所以函数可以像变量一样出现在top-level。
动态类型
Dart并非脚本语言,所以这里的“动态类型”是指Java的Object
或者Kotlin的Any
那样的不确定类型,可以在运行时强转成任何具体类型使用。Dart使用Object
或者dynamic
声明这种不确定类型。
dynamic
和Object
非常相近,都可以动态进行类型检查、动态赋任何类型的值
/// 动态判断dynamic对象
bool convertToBool(dynamic arg) {
if (arg is bool) return arg;
if (arg is String) return arg == 'true';
throw ArgumentError('Cannot convert $arg to a bool.');
}
他们的区别主要在与静态检查上:
dynamic
:编译器不会对dynamic象进行静态检查,dynamic对象的任何操作在编译期都是合法的,但是运行时是否会出错需要开发者自己保证,所以建议大家在编程时不要不过度使用dynamic。Object
: 是Dart对象的基类,当你定义Object o = xxx ;
时 ,编译器会将o
作为一个Object
对象看待,可以以调用o
的toString()
和hashCode()
方法,因为Object
提供了这些方法,但是如果你尝试调用o.foo()
时,静态类型检查会运行报错。
Object
就是Java的Object,dynamic
更像是Javascript的var
。有趣的是Dart也有var
关键字,但是Dart的var
与Js的var
不同,一旦赋值之后类型就不能再改变了,所以Dart的var
更像是Kotlin的var
。
根据 Effective Dart 的建议:
- 当实例代表“任何类型”时,使用
Object
; - 当实例代表“某种待定类型时”时,使用
dynamic
,可能在某个时间点确定
访问控制
通过在函数、变量、类之前添加前导下划线(_
)表示其私有性,类似于Java的private
关键字,看起来像是借鉴了python
的语法。
需要注意这里的私有不是指package的私有,而是library的私有。library内部即使添加了前导下划线也是可以互相访问的,例如一个Class内的_
函数仍然可以被Class外部访问,library内部的访问控制需要使用linter
关键字
类型推导
Dart像Kotlin一样,可以根据上线文推断出变量类型,而不必事先显式地进行类型声明
var hoge = "huga";
[1, 2, 3].forEach((x) => print(x*2));
Dart能够推断出hoge是String
,x是int
。
主要注意,当指定类型声明时,类型需要像Java前置声明,而不是像Kotlin一样后置。
变量默认值
- Dart中万物皆对象,都继承自Object,所以默认值都是
null
,包括int
和double
。 - Java中存在非对象的基本型,所以默认值不是
null
; - Kotlin的基本型虽然本质上也是对象,但是其默认值并不是
null
。
使用assert进行判空
因为Dart的默认值null的存在,写代码时需要注意判空。Dart的assert()
不会影响生产环境中代码的执行,它仅仅在测试环境中起作用,实际开发中可以活用assert进行判空。
Dart 2.7引入了NNBD (Non null by default)
,可以像Kotlin一样声明非空类型,将NPE扑灭在编译期。
NNBD
从 2.7 开始Dart像Kotlin一样允许声明非空类型
// int是non-null类型
final int a = 1;
// non-null类型赋值null会出现error
final int b = null;
// int?是nullable类型,可以赋值null
final int? c = null;
我们在命名参数前加required
关键字,表示此参数一定能取到值
// arg1 :non-null的String类型
void doSomething({required String arg1}) { ... }
// 调用函数时,required的参数不可省略
doSomething(arg1: "hoge");
可以在flutter项目根目录创建一个analysis_options.yaml
文件,然后通过添加以下内容,对空安全进行静态检查
analyzer:
enable-experiment:
- non-nullable
不可变类型与常量
final
类似于Java的final
或者Kotlin的val
,使用final
声明的变量是不可变的,不能被二次赋值。
final hoge = "fuga";
虽然与Java的用法相近,但是细节上更严格:
- Java的
final
只规定不能二次赋值,但是没有规定初始化的时机 - Dart的
final
要求必须在声明的同时或者在初始化列表
中完成初始化。
Class的final
成员可以在初始化列表
中初始化:
mport 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x, //初始化列表中对final成员初始化
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
const
const
是编译期必须确定的常量,相当于Kotlin的const val x
- 而
final
是可以在运行时在进行第一次初始化:
final x = new DateTime.now(); // 正确
const x = new DateTime.now(); // 错误
final y = sin(90); // 正确
const y = sin(90); // 错误
另外,const
还可以进行字面值的list创建:
var hoge = const [1, 2, 3];
此时hoge是一个immutable
的list,内部的item不能变更