[TOC]
定位
首先本文是通过对比其他语言(主要是java)快速入门,建议您至少要熟悉一门静态语言或者动态语言。Dart和java以及C#都差不多,基本上不用学习可以直接使用,从这里可以你可以了解Dart有些特别之处。
首先从一段代码入门吧:
void main() {
for (int i = 0; i < 5; i++) {
printTest('hello ${i + 1}');
}
}
void printTest(String variableInteger) {
print(variableInteger);
}
从上面可以看到,Dart语言主入口是main函数
,他可以不用属于某个类。一眼看上去和java差不多。流程控制也是由小括号和大括号构成,不用考虑缩进。结尾必须要加上分号。
声明数据类型
void main() {
int l = 4;
String str1 = "str1";
var i = 1;
dynamic d = 0;
final j = 2;
const k = 3;
}
Dart是强类型语言,变量都会有一个类型。你可以向java那样直接声明类型,同样可以像swift或者Kotlin那样使用类型推导。Dart的类型推导使用final,var,const,dynamic关键字。
-
var
修饰变量,这个没什么好说的。 -
final
表示不可变的,修饰内置数据类型,值不可变;修饰对象表示引用不可变,使用到的频率很高。 -
const
是编译时常量,他表示始终不可变,无论修饰内置类型还是对象,或者是数据结构。 -
dynamic
是任意类型,有点像java里面的Object
,Kotlin中的Any
。
使用const修饰的都是编译时常量。它区别于运行时,它的值是不可变的。
所有的数据类型,不管是int还是String,初始值都是null
数据类型
numbers、strings和booleans
首先是numbers
,strings
,booleans
其中numbers包括int
和double
类型,分别表示整数和浮点数。strings就是String
类型,普通的字符串。booleans类型是bool
只包含true
和false
。
numbers
* int
* double
strings
* String
booleans
* bool
如下代码:
void main() {
// numbers
int i = 1;
double d = 1.1;
// 字符串转数字
// 这里就使用了类型推导
final p1 = "3";
final p2 = "3.3";
print(int.parse(p1));
print(double.parse(p2));
// string
// 可以使用('xxx')("xxx")('''xxx''') 三种方式初始化字符串,和Kotlin很像。
// 字符串比较的是值
final str1 = "test";
final str2 = "test";
print(str1 == str2); // true
final str3 = 'test';
print(str3);
final str4 = '''test''';
print(str4);
print(str4 == str3); // true
print("I'm ${str4}!!!");
//booleans
// null 可以直接用来判断,它是false
if(null) {
print('null is true');
} else {
print('null is false');
}
print(''.isEmpty);
// final list = [];
// if(!list) {
// print('not empty');
// }
}
Dart的数据结构包括list和map
list
list
对应的就是java里面的List,list可以像Python等语言那样使用[]
进行数组操作,参照以下示例代码。
List有几个在flutter非常常用的初始化方式,分别为filled
和generate
。具体参考:https://api.dartlang.org/stable/2.1.0/dart-core/List-class.html
void main() {
// 初始化
final list1 = [1,2,3,4,5];
// 编译时常量
final list2 = const [2,3,4,5,6];
// list2[2] = 8; error
print(list2);
// 初始化2
List<int> list3 = new List(5);
print(list3); // [null, null, null, null, null]
// 初始化3 filled 第一个参数是个数,第二个是添加的值
List<int> list4 = List.filled(3,10);
print(list4); // [10,10,10]
// 初始化4 类似Python的Map生成
List<int> list5 = List.generate(3,(i) => i * 3);
print(list5); // [0, 3, 6]
// 更改 使用list1
list1[2] = 8;
print(list1); // [1, 2, 8, 4, 5]
// 添加
list1.add(10);
// list1[10] = 20; Index out of range
print(list1); // [1, 2, 8, 4, 5, 10]
//删除
list1.remove(10);
print(list1); // [1, 2, 8, 4, 5]
}
map
map
对应java里面的Map,他的操作方式有点像Python。需要注意的有几点。
- Map的Key不能有重复的
- 如果使用int类型作为key,取值的时候优先使用key匹配,然后是下标。
- 取不到值不会抛异常,直接返回null
- Map和List可以转换为Json,来回转换时深拷贝。
其他API参考:https://api.dartlang.org/stable/2.1.0/dart-core/Map-class.html
import 'dart:convert';
void main() {
final map1 = {
"test1key" : "test1value",
// "test1key" : "test1value", Key 不能相同
123 : "123value",
null : 'NULL',
2 : '2'
};
print(map1); // {test1key: test1value, 123: 123value, null: NULL, 2: 2}
// 获取值
print(map1[123]); // key取值 123value
print(map1[1]); // 下标取值 null
print(map1[2]); // 同时存在时候,优先Key取值;结果为 2
// 追加值
map1['appendKey'] = 'appendVal';
print(map1);
// 更改值
map1['appendKey'] = 'appendVal123';
print(map1);
map1['appendKey'] = null;
print(map1); // 置为null之后,key不会消失
map1.remove('appendKey');
// 删除
print(map1);
print(map1['non-key']); // 不会报错 打印 null
print(map1.length); // 长度 4
// json 转换
// 使用const修饰的数据结构,不可改变。
final map2 = const {
"name":"lecon",
"age":23,
"ss":[1,2,3,4,4]
};
final jsonText = jsonEncode(map2);
print(jsonText); // {"name":"lecon","age":23,"ss":[1,2,3,4,4]}
final mapObj = jsonDecode(jsonText);
print(mapObj); // {name: lecon, age: 23, ss: [1, 2, 3, 4, 4]}
}
Runes
这东西其实就是字符集的扩编,可以用它显示一些表情符号
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
函数
Dart的函数比java多了几个概念。比如在参数列表里面添加个大括号,是可选命名参数
;加个中括号叫可选位置参数
。
- 可选命名参数类似Python里面的**kwargs,参考下面示例代码
func3
; - 可选位置参数类似Python里面的*args,参考下面
func5
。 - 可选命名参数和可选位置参数可以有默认值,普通参数不能有,参考
func6
。 - 函数之间可以互相嵌套,互相调用,但他们都有自己作用域。
- 若果函数简单可以使用
()=> value
简写。
比如
String test() {
return "123";
}
简写成
String test ()=> "123";
下面是示例代码
// import 'package:meta/meta.dart'
void main() {
func1(123);
func2('222');
func3("lecon");
// func3("lecon","spawn",123); error,使用下边的中括号,注意区别。这个使用key区分,下面使用位置区分
func3("lecon",param2:"spawn",param3:123);
// func4("lecon",param2:"spawn"); error required
func5("lecon","spawn","hahaa");
func6("lecon",age:25);
print(func7());
}
void func1(param1) {
print(param1);
}
void func2(String param2) {
print(param2);
}
void func3(String param1,{String param2,int param3}) {
print("I am ${param1}, Hi ${param2},I am ${param3}");
}
// test in flutter
// void func4(String param1,{String param2,@required int param3}) {
// print("I am ${param1}, Hi ${param2},I am ${param3}");
// }
void func5(String param1,[String param2,String param3]) {
print("I am ${param1}, bian bu ${param2} xia qu le ${param3}");
}
// 只有使用{}和[]的才可以有默认值
void func6(String param1,{int age = 23}) {
print("I am ${param1}, I am ${age}");
}
func7() {
void func8() {
}
void func9() {
}
retrun () => "123";
}
流程控制
Dart的流程控制和java的用法基本一样,这里就不介绍了。
有个for in要说下,和JavaScript很像。
for (var x in collection) {}
相当于java的高级for循环
for(Integer i : indexs) {}
面向对象
相对于java有这几点需要注意
-
new
关键字可以省略 - 一个类同名构造器只能有一个,也就是说可以给构造器起其他名字。所以可以把构造器分为
默认构造器
和命名构造器
。 - 命名构造器不能自动继承,参考下面
- Dart使用
this
代表当前对象 - 初始化列表
- Dart有继承和接口概念,和java基本差不多,同时多了个
混入
的的概念minix
,这个非常好理解。
下面这个例子是类构造器的基本使用
普通构造器和继承
class Person {
String name;
int age;
String dantengt;
// 注意,冒号后边是初始化列表,用,隔开。
Person() :dantengt = "danm";
@override
String toString() => "name : ${name};age : ${age}";
}
class Person2 {
String name;
int age;
// 默认构造器只能声明一个
// 不用写set方法,使用this可以直接把值赋给相应的属性
Person2(this.name,this.age);
// 命名构造器
Person2.copy(Person p) {
this.name = p.name;
this.age = p.age;
}
@override
String toString() => "name : ${name};age : ${age}";
}
class Student extends Person2 {
final String school;
// 普通构造器继承
Student(String name,int age,this.school):super(name,age);
// 命名构造器的继承
Student.copy(Person p,this.school):super.copy(p);
// 类似构造器的重载
Student.mySchoolCopy(Person p):this.copy(p,'mySchool');
}
// 所有值都不可以改变
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
// cosnt修饰的构造器是常量构造器,里面的值都不许改变。因为是编译时的。
const ImmutablePoint(this.x, this.y);
}
void main() {
final person = Person();
person.name = "spawn";
person.age = 18;
print(person);
final person2 = Person2("lecon",25);
print(person2);
print(Person2.copy(person));
Person2 p2 = Student("lecon",25,"haha");
// is 和 as ;类似 instanceof和强转
if (p2 is Student) {
(p2 as Student).school;
}
}
其中:
- Person类的构造器后面有个
:
,那个就是初始化列表,可以给属性赋值(参考Person类),还可调用父类构造器。 - Person2 类有两个构造器,默认构造器没有名字,和java中一样使用类名声明。另外还有一个
Person2.copy()
他也是一个构造器,只不过它有名字了,同名构造器只能出现一次,不管是不是重载(貌似Dart没重载)。 - 在构造器参数列表中,如果直接使用
this
关键字,可以直接把值付给类的属性,省去了set方法(参考类Person2的默认构造器)。 - 在类中,所有的属性或者方法,只要带了
_
前缀,那么他就是私有的,Dart文件之外不能访问,当前文件不同类是可以的。 -
is
关键字相当于instanceof
,as
相当于强转。这个和Kotlin很像。 -
const
修饰构造器,代表类是编译时的。所有内容不允许改变。 - 类的继承使用
extends
关键字,同时dart也有接口
,和java基本一致,不再赘述。 - 除了继承和接口之外,Dart还有个
mixin
混入的概念,可以把他理解为:把其他类的东西,一股脑放到当前类中,使用with
关键字描述。
minix
关于minix参考以下代码:
// class关键字换成minix,其他和class一样,只是换个名字。
mixin Play {
void play() {
print("I can play");
}
}
mixin Eat {
void eat() {
print("I can eat");
}
}
// 使用with关键字加入mixin类
class Person with Play,Eat {
final String name;
final int age;
Person(this.name,this.age);
}
void main() {
final p = Person("lecon",13);
// 此时p有了Eat和Play的能力
p.play();
p.eat();
}
factory构造器和多态
java编程中,在使用多态的时候,我们可能会用到工厂方法,dart给我们提供一个方便,使用dart的工厂构造器。dart的工厂构造器也是个构造器,只不过使用factory修饰,他返回当前类,并且不能使用this关键字,示例代码。
class Person2 {
String name;
int age;
Person2(this.name,this.age);
// factory 构造器也是构造器,同样不能声明两个相同名字的,而且只能有一个默认
// factory 没有this引用
factory Person2.select(name,int age,int type) {
if(type == 0) {
return Student(name,age,"jaja");
} else {
return Worker(name,age,"lala");
}
}
// callable
call(String a, String b, String c) => '$a $b $c!';
@override
String toString() => "name : ${name};age : ${age}";
}
class Student extends Person2 {
final String school;
Student(String name,int age,this.school):super(name,age);
}
class Worker extends Person2 {
final String company;
Worker(String name,int age,this.company):super(name,age);
}
void main() {
// 不关注Person2实现类,直接使用Person2实例化。使用factory
final p1 = Person2.select("lecon",23,0);
final p2 = Person2.select("lecon",23,1);
print(p1.runtimeType); // Student
print(p2.runtimeType); // Worker
// callable
print(p1("123","234","345"));
}
其中select
为Person2的工厂构造器,由它来返回对应的实现类。
setter和getter
很多语言里面,为了方便获取属性都提供了setter和getter。其实他就是个语法糖。直接使用就可以。看例子:
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
异常
Dart的异常也和java非常像。直接看例子
void main() {
// throw1();
// throw2();
// throw3();
// throw4();
throw5();
}
void throw1() {
throw FormatException('Expected at least 1 section');
}
void throw2() {
throw 'Expected at least 1 section';
}
void throw3() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
}
try {
throw FormatException('Expected at least 1 section');
} on FormatException catch(e) {
print('section exception ${e}');
}
try {
throw FormatException('Expected at least 1 section');
} on FormatException catch(e,s) {
print('section exception ${e}');
print(s); // 打印方法调用栈
}
}
void throw4() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
rethrow; // 重抛
}
}
void throw5() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
} finally {
print("I am finally");
}
}
- dart可以直接抛出默认异常,
throw 'Expected at least 1 section';
- 使用
on
来匹配异常类型,on FormatException
- 使用
catch
来获取异常变量,catch(e,s)
它有一个默认参数和可选参数。分别为信息和调用栈。 - 使用
rethrow
重新抛出异常
异步
dart的异步和JavaScript ES6差不多,使用await
和async
,只不过ES6中的Promise变成了Future。目前比较流行的解决方案都是这个,比如Python,Kotlin都这么使用。一般在网络请求或者数据库操作时候使用,就像java的多线程基础版吧。
异步这块东西有点多,而且很重要,直接贴官方地址。https://www.dartlang.org/guides/libraries/library-tour#future
异步中有两个很重要的接口:Future
和Stream
。贴出地址,这两个一定要弄明白。https://www.dartlang.org/guides/libraries/library-tour#dartasync---asynchronous-programming
生成器
Dart的生成成器和其他语言里面的生成器差不多,分为同步
和异步
两种。同步生成器结构是Iterable
,异步生成器接口是Stream
。流程控住使用async*
(异步)和sync*
(同步)声明,使用yield
流程控制,每一次yield
都会给列表生成一个值,也就是说生成器返回结果的数据结构可以当列表使用。以下代码:
void main() {
print(naturalsTo(10)); // (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(naturalsDownFrom(10)); // (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
asynchronousNaturalsTo(10)
.toList() // toList把异步转为同步
.then((res) {
print(res); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
});
}
// 同步生成器
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
// 异步生成器
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
// 递归性质的生成器
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
其他
import关键字
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// 仅导入foo
import 'package:lib1/lib1.dart' show foo;
// 排除foo
import 'package:lib2/lib2.dart' hide foo;
// 异步导入
import 'package:greetings/hello.dart' deferred as hello;
空判断
Dart也有null
类型,并且和其他语言类似,Dart也是用?
来解决空问题。
void main() {
final a = null;
print(a != null ? a : "b"); // 等同于下边,dart也支持三元运算符
print(a ?? "b"); // 打印 b
String p = null;
print(p?.toString()); //打印null,而不会空指针
}
..
运算符
Dart的联级运算符..
很有意思,很实用的语法糖,直接看代码你就明白了。Builder
大法好。
void main() {
// 联级运算符
final p = Person()
..username = "lecon"
..password = "123456"
..setSex(true);
print(p); // Username: lecon; Password: 123456; Sex: true
}
class Person {
String _username;
String _password;
bool _sex;
set username(String username) => this._username = username;
set password(String password) => this._password = password;
void setSex(bool sex) => this._sex = sex;
@override
String toString() => "Username: ${this._username}; Password: ${this._password}; Sex: ${this._sex}";
}
Typedefs
这个和C++里边的内联函数基本差不多。
官方例子 https://www.dartlang.org/guides/language/language-tour#typedefs
注释
Dart注释分为三种,单行注释和多行注释和java一致。doc注释使用 ///
表示。
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
最后
临时总结,难免有错误,请多多指出,我会在第一时间改正。
代码地址
https://gist.github.com/leconio/73bd75eef9530ff76ac59e09b1331865