Dart对比java快速入门

首先本文是通过对比其他语言(主要是 java)快速入门

定位

首先本文是通过对比其他语言(主要是 java)快速入门,建议您至少要熟悉一门静态语言或者动态语言。Dart 和 java 以及 C#都差不多,基本上不用学习可以直接使用,从这里可以你可以了解 Dart 有些特别之处。

首先从一段代码入门吧:

1
2
3
4
5
6
7
8
9
void main() {
for (int i = 0; i < 5; i++) {
printTest('hello ${i + 1}');
}
}

void printTest(String variableInteger) {
print(variableInteger);
}

从上面可以看到,Dart 语言主入口是main函数,他可以不用属于某个类。一眼看上去和 java 差不多。流程控制也是由小括号和大括号构成,不用考虑缩进。结尾必须要加上分号

声明数据类型

1
2
3
4
5
6
7
8
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 包括intdouble类型,分别表示整数和浮点数。strings 就是String类型,普通的字符串。booleans 类型是bool只包含truefalse

numbers
* int
* double

strings
* String

booleans
* bool

如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 非常常用的初始化方式,分别为filledgenerate。具体参考:https://api.dartlang.org/stable/2.1.0/dart-core/List-class.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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

这东西其实就是字符集的扩编,可以用它显示一些表情符号

1
2
3
4
5
6
7
8
9
10
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简写。

比如

1
2
3
String test() {
return "123";
}

简写成

1
String test ()=> "123";

下面是示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 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 很像。

1
for (var x in collection) {}

相当于 java 的高级 for 循环

1
for(Integer i : indexs) {}

面向对象

相对于 java 有这几点需要注意

  • new关键字可以省略
  • 一个类同名构造器只能有一个,也就是说可以给构造器起其他名字。所以可以把构造器分为默认构造器命名构造器
  • 命名构造器不能自动继承,参考下面
  • Dart 使用this代表当前对象
  • 初始化列表
  • Dart 有继承和接口概念,和 java 基本差不多,同时多了个混入的的概念minix,这个非常好理解。

下面这个例子是类构造器的基本使用

普通构造器和继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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关键字相当于instanceofas相当于强转。这个和 Kotlin 很像。
  • const修饰构造器,代表类是编译时的。所有内容不允许改变。
  • 类的继承使用extends关键字,同时 dart 也有接口,和 java 基本一致,不再赘述。
  • 除了继承和接口之外,Dart 还有个mixin混入的概念,可以把他理解为:把其他类的东西,一股脑放到当前类中,使用with关键字描述。

minix

关于 minix 参考以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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 关键字,示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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。其实他就是个语法糖。直接使用就可以。看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 非常像。直接看例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 差不多,使用awaitasync,只不过 ES6 中的 Promise 变成了 Future。目前比较流行的解决方案都是这个,比如 Python,Kotlin 都这么使用。一般在网络请求或者数据库操作时候使用,就像 java 的多线程基础版吧。

异步这块东西有点多,而且很重要,直接贴官方地址。https://www.dartlang.org/guides/libraries/library-tour#future

异步中有两个很重要的接口:FutureStream。贴出地址,这两个一定要弄明白。https://www.dartlang.org/guides/libraries/library-tour#dartasync---asynchronous-programming

生成器

Dart 的生成成器和其他语言里面的生成器差不多,分为同步异步两种。同步生成器结构是Iterable,异步生成器接口是Stream。流程控住使用async*(异步)和sync*(同步)声明,使用yield流程控制,每一次yield都会给列表生成一个值,也就是说生成器返回结果的数据结构可以当列表使用。以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 关键字

1
2
3
4
5
6
7
8
9
10
11
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 也是用来解决空问题。

1
2
3
4
5
6
7
8
void main() {
final a = null;
print(a != null ? a : "b"); // 等同于下边,dart也支持三元运算符
print(a ?? "b"); // 打印 b

String p = null;
print(p?.toString()); //打印null,而不会空指针
}

..运算符

Dart 的联级运算符..很有意思,很实用的语法糖,直接看代码你就明白了。Builder大法好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 注释使用 ///表示。

1
2
3
4
/// 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


 Gitalk评论