Dart
我们有不得不学的理由,因为有Flutter
这尊大神在啊,但是像有的大神这样潇洒,现阶段我还做不到
如果您熟悉面向对象和基本编程概念(如变量、循环和条件控制),则可以完成本教程,您无需要了解Dart或拥有移动开发的经验
膜拜大神...... 初上Dart
有种似曾相识的感觉,那是因为现在高阶语言基本趋同,Dart
又是融合强类型语言和弱类型语言,所以还是不能直接跳过,要不然很多代码其实也是不怎么清楚的。再者对我自己来说,若是不把Dart
系统的走一遍,还是会有朦胧感的,这样很不舒服的
本文重在给初次接触Dart
的朋友提供帮助,内容涵盖基础语法,但是Dart
的多线程、异步不在本文之列,那是下一篇的内容。同时呢自己上手操作下、再写一遍笔记,这样记得清楚,更是为以后着想,人过留名,雁过留声
,我们学完了不写博客怎么证明我们学过了,以后忘了怎么办,那就是白学了嘛,希望大家都自己做笔记写博客、写文章
我写的很随自己的意思,大家看不惯的话请看下面写的很标准的:
Dart 特性
网上很多文章都说 Dart 在java 的基础上融会了高级语言的特性,但是我想说 java 最新版本就没有吗?所以这样说等于没说
要我说 Dart = java + kotlin,要是您接触过 python,那么 Dart = java + python 才是最恰当的,在 Dart 中您即可以编写传统 java 风格的代码,也可以编写类似 python 风格的代码,下面我尽量给大家展示出来 Dart 在那些方面像 python
1. 像 python 的地方
- 1.1 Dart 支持库形式开发
用过python
的同学都知道是怎么回事,在java
里我们创建一个class
文件必须要在该文件内创建一个和该文件同名的类
public class Name {
......
}
但是Dart
允许我们在Dart
文件种不创建类,直接写属性、写方法,然后以库的方式给别人使用
DartLib 代码:
var name = "NAME";
void function1() {
print(name);
}
void function2() {
print("BBB");
}
以库的形式导入使用
import 'package:flutter_app4/DartLib.dart' as Lib;
void test(){
Lib.name = "FUN";
Lib.function1();
}
- 1.2 Dart 允许大家像 python 那样声明变量
var book = Book("AA", 18);
- 1.3 Dart 可以像 python 一样,方法声明时返回值可写可不写,编译器会自动判断返回类型的
sum( var num1){
return num1 + 10;
}
- 1.4 Dart 和 python 最大的区别:就是 Dart 不能像 python 那样可以处处省略参数类型,Dart 必须得写参数类型,
{}、;
也必须写,要不然 Dart 就真的和 python 差不多了
2. Dart 支持 var 动态类型
Dart
静动态类型都支持,再加上高级语言特性,这样就可以像怎么写就怎么写了,即可以写成传统的java
面向对象、处处皆对象的代码风格,也可以像kotlin
一样,还可以像python
一样,现在语言的差异越来越小了,Dart
和python
的差异很小了,说实话,我是比较喜欢python
的,极简是有种魔力的,粘上就忘不了了
3. Dart 有性能优势
Dart可以在没有锁的情况下进行对象分配和垃圾回收。就像JavaScript一样,Dart避免了抢占式调度和共享内存(因而也不需要锁),所以Dart可以更轻松地创建以60fps运行的流畅动画和转场。这个在你使用Dart开发出App之后必定会深有感触
基本数据类型
Dart
基本数据类型:
- 动态类型 -
var
、dynamic
- 数值型 -
Num
、int
、double
、bool
(布尔值) - 集合型 -
List
、Map
,Dart 没有数组的概念,数组就等于集合 - 文本型 -
String
- 方法类 -
Function
下面仔细说下:
1. var
和dynamic
是动态类型
2. 新的数学类型 Num
int
、double
大家知道是什么,那这个Num
是仅限于数字的动态类型,数值可以是int
也可以是double
num name = 10;
void test() {
name = 11;
name = 11.2;
}
3. Dart 没有 kotlin 这么变态的非 null 要求,声明的变量初始值可以给 null
var name ;
4. var 的特性
var 声明的变量,初始值要是 null 的话,后面给什么值都可以,但只要 var 的变量给初始值明确类型了,那么后面就只能是这个类型的数据了
// 这样 OK
var name ;
void test() {
name = 11;
name = "AA";
}
// 这样报错
var name = 10;
void test() {
name = 11;
name = "AA";
}
5. dynamic
dynamic
是不确定类型和var
是一样的,但是var
是给开发人员用的,dynamic
是Dart 内部用的,我们也可以写dynamic
,但是一般有dynamic
的地方都自动隐藏了,dynamic
这个关键字自动提示是没有的,就是说Dart
不建议我们使用dynamic
,我们知道dynamic
什么意思就行了:dynamic
修饰的变量,可以赋给任何类型的值
运算符
常用的就不说了,说些有变化的
1. /
在java中取整
,5/3=1;但是在Dart中就不是取整了,而是带小数点了,5/3=1.6666...
2. ~/
-
在Dart中是取整的意思,5/3=1
3. ==
Dart 的==
判断的就不是引用一样不一样了,而是判断的等号2边内容一样不一样
4. identical(name1, name2)
identical
在Dart中才是判断应用一样不一样的
5. ??=
给null赋值,若这个参数==null,那么就=后面的这个数,当然大家使用??
也是可以的,??
两边可以接表达式的
var name;
print(name ??= 10);
~~~~~~~~log~~~~~~~~~~~
I/flutter: 10
6. =>
单行时可以省略{}
Function test1 = (var name, var age) => print(name + age);
7. ..
进行链式调用,非常简便
var book = Book("AA", 12)
..name = "BB"
..age = 15;
8. ?.
null的话不报错
var book = Book("AA", 12, "CC");
book?.name = "BB";
9. is
、is!
、as
类型判断,转换类型,详细不展开了,大家都知道
if (book1 is Book) {
book1.name = "AA";
}
(book2 as Book).name = "DD";
switch
Dart 中的 switch case 支持基本数据类型和对象类型,另外还可以给每个 case 加上别名,用于指定跳转到某个 case 执行代码
var name = "BB";
switch (name) {
as:
case "AA":
print("AA");
break;
case "BB":
print("BB");
continue as;
break;
case "CC":
print("CC");
break;
}
~~~~~~~~~log~~~~~~~~~
I/flutter: BB
I/flutter: AA
比如上面我们给AA这个case加上as这个标记之后,在BB中我们就可以使用continue
这个关键字跳到AA这个case去执行代码,不管AA符不符合判断,后会执行AA这个case 的代码
方法上的变化
1. Dart 中方法都是有返回值的,即便我们写void
其实也是返回的null
,这点大家知道一下就行
2. 参数花样
Dart 用{}
表示可选参数,用[]
表示位置参数,没 python 好使
// 可选参数,用 xx:xx 传值
void test1(var name1, {var name2 = 10, var name3 = 20}) {
print("result = $name1,$name2,$name3");
}
// 位置参数,位置是固定的,你要是想给 name3赋值,必须先给name2赋值
void test2(var name1, [var name2, var name3]) {
print("result = $name1,$name2,$name3");
}
test1("AA", name2: 55);
test2("BB",18,19);
3. 方法皆对象
Dart 秉承高级语言的特性,一切解释对象,方法本身也不例外,熟悉 koltin、python 的朋友一定不会谋生
void man(){
print("man");
}
var f = man;
f();
比如上面,我们可以声明一个属性对象,用别的方法赋值给该对象,然后加()
的方式运行该方法
4. 参数方法
我们来看下 Dart 中方法作为参数的使用
// 声明方法
void textView(void click(var name)) => click("AA");
// 使用方法
textView((var name) => print(name));
5. 匿名方法
其实在上面4
中就展示了匿名方法,这里再强调一下,Dart 中匿名方法语法:返回值 (参数类型 参数名,xxx){}
,没有返回值的可以不写,实现单行可以用=>
代替{}
举个例子:
void textView(String click(var name)) => click("AA");
textView((var name) {
print(name);
return "AA";
});
6. 闭包
Dart 的闭包
和 JS、python 一个意思,是定义在方法里面的方法,其特性:
- 闭包是一个对象
- 闭包定义在其他方法内部
- 闭包可以访问外部方法的布局变量,并持有其状态。这是闭包的最大用途,通过闭包的方式把一个方法内部的属性暴露出去,因为方法是可以返回方法的,Dart 中方法也是对象一样可以当做返回值处理
很多人说闭包
学不会,看了很多资料还是不明白,其实吧这样跟大家说:大家把闭包看成对象就行了
比如下面这个例子,我们拿到闭包,操作外层方法的数据不停的自增,我们对调用几次闭包,大家会发现Z合格数据是不停增加的
// 闭包
sum(){
var num = 0;
sum2(){
num++;
print(num);
}
return sum2;
// 也可以这么写,匿名的方式用的最多
return (){
num++;
print(num);
};
}
// 多调用闭包几次
void main(){
Function a = sum();
a();
a();
a();
a();
a();
a();
}
~~~~~~~~~~log~~~~~~~~~~
1
2
3
4
5
6
结果是不会骗人的,大家就把闭包看成是一个包含外层数据的匿名实现类,当成对象看就好理解了,这是最经典的一个例子
面向对象上的变化
1. 可以省略 new
上文说过 Dart 创建对象可以省略new
,很棒的设计,体验上像 python 极简主义靠拢
var book = Book("AA", 12);
2. Dart 默认自带get/set
方法
3. Dart 中方法是不能重载的
4. Dart 没有public,private
Dart 的可见范围是以library
库为单位的,每一个 Dart 文件就是一个库,Dart 中_
表示库级别私有,_
即可以修饰变量,也可以修饰对象,还可以修饰方法,这点又是和 python 一样
// 这样的对象,除了这个库文件,哪也别想用,变量我就举例了
class _Book {
var name, age;
_Book(this.name, this.age);
speak(){
}
}
5. 计算属性
Dart 属性默认自动生成get、set
方法,但是大家也可以自己写,但是自己写的get、set
就有区别了,看下面:
class RectTest {
var width, height;
RectTest(this.width, this.height);
get area => width * height;
set area(value) => width = value;
}
void main() {
var rect = RectTest(20, 12);
print(rect.area);
rect.area = 30;
print(rect.area);
print(rect.width);
}
~~~~~~~~~~~~~log~~~~~~~~~~~~~~~
240
360
30
- 要自己写
get、set
的话,该属性就不能声明,写了get、set
方法的话就当是生命了一个变量,该变量set
方法中不能给自己赋值(会陷入无限递归的状态),只能给别的属性赋值,大家看例子中的结果也能看出来,可以自己试下
6. 构造方法
Dart 中构造方法变化较大
- 6.1 首先 Dart 像 java 一样提供一个默认的空的构造方法,但是一旦我们自己实现构造方法的话,这个默认的构造方法就会失效,我们自己的写的构造方法会替代默认的构造方法,另外 Dart 也提供了便利的语法糖在里面
class Book {
var name, age;
// 标准写法
Book(var name, var age){
this.name = name;
this.age = age;
}
// 语法糖1
Book(this.name, this.age){
}
// 语法糖2
Book(this.name, this.age);
}
有一点要明确,这样的构造方法里,this的赋值是在构造方法方法体之前执行的
- 6.2 Dart 蛋疼的不支持重载,所以 Dart 提供了
命名构造方法
,就是类名+方法名
的方式实现多构造方法
class Book {
var name, age;
Book(this.name, this.age);
Book.withName(this.name);
Book.withAge(this.age);
}
然后自动提示就可以显示有多个构造方法了
- 6.3 常量构造方法,若是有一个对象他的值都不需要变化的话我们就可以使用常量构造函数构造一个常量,这样的话对象的值在编译期就能知道,运行速度上有优势,这个需要结合 const 使用,具体场景估计也就是枚举了吧
// const 的对象要求属性都是不可变的,也就是 final 的
class Book {
final name, age;
const Book(this.name, this.age);
}
void main() {
const book = const Book(12,112);
}
- 6.4 工厂构造方法,在普通构造方法前加
factory
关键字,这个是配和私有构造方法使用的,看下面的例子
class Book {
var name, age;
factory Book(var name){
return Book._withName(name);
}
// 私有构造方法
Book._withName(this.name);
Book._withAge(this.age);
}
- 6.5 给 final 赋值,final 描述的一般都是不可变的值,Dart 允许我们声明 final 时给个null,但是在代码运行前必须初始化,这个地方就是构造方法,方式有2种:
方式1:在构造方法后面跟:
给 final 赋值
class Book {
var name, age;
final title;
Book(this.name, this.age, var title) : this.title = title {}
}
方式2:直接在构造方法中赋值,利用this.xxx
在方法体之前执行的特性
class Book {
var name, age;
final title;
Book(this.name, this.age, this.title);
}
- 6.6 对象
call
方法,高级语言中允许把方法当做对象使用,Dart 中允许大家把对象当方法使用book1()
,本质是调用book1里面的指定方法call
class Book {
var name, age;
final title;
Book(this.name, this.age, this.title);
call( var value ){
print("$name同学请$value同学上台演讲");
}
}
void main() {
var book1 = Book("AA", 12, "CC");
var book2 = Book("AA", 12, "CC");
book1("PP");
}
~~~~~~~~~~~~~~~log~~~~~~~~~~~~~~~~~~
AA同学请PP同学上台演讲
其实什么++,--
之类的都是运用的这个思路,不过不推荐这个搞,这样会模糊对象和方法的边界,那代码就不好阅读理解了
7 继承
Dart 是单继承关系,子类不能继承父类的私有(属性、方法)和构造函数
Dart 最蛋疼的是就是继承关系中的构造函数了,Dart 的子类默认使用父类的默认构造函数,若是父类没有默认的构造函数了,而是用户自定义的,那么子类必须显示的继承父类的一个构造函数,看下面的例子:
class Animal {
var name, sex;
Animal(this.name);
Animal.withSex(this.sex);
}
class Dog extends Animal {
Dog(name) : super(name);
Dog.withSex(sex) : super.withSex(sex);
}
情况再复杂一点的是加上 final 赋值,中间用,
相联
class Dog extends Animal {
final price;
Dog.withSex(var sex, var price)
: this.price = price,
super.withSex(sex);
}
8. 多继承
Dart 中多继承专门有个词:Mixins
,Dart 允许我们使用with
关键字实现多继承,但是多继承本身非常危险,很容易出现各种问题的
标准写法:
class Animal {
var age, sex;
}
class Cat {
var name;
speak() {
print("CAT");
}
}
class Dog {
var name;
speak() {
print("DOG");
}
}
class Bear {
var name;
speak() {
print("BEAR");
}
}
class MyPet extends Animal with Dog, Cat, Bear {
var n;
MyPet(this.n);
}
然后我们调用下 pet.speak() 方法看看
var pet = MyPet();
pet.speak();
~~~~~~~~log~~~~~~~~
BEAR
这说明对于多继承来说,多个父类同名的属性和方法是以最后一个继承的父类的数据为准
并且多继承对父类要求很严格,要求父类不能有自己的构造方法,默认的除外...蛋疼不,并且多继承很容易出问题的,使用时要格外注意
多继承进一步就是组合了,用于模块间和架构内的功能组合,看下面的代码,这块我也没有什么体验,也是道听途说
class MyPet1 = Animal with Dog;
class MyPet2 = Animal with Cat;
class MyPet3 = Animal with Bear;
我有3种不同功能的宠物,注意这样写的话要求 MyPet1 这样的子类不能有自己的属性和方法,纯粹是为了功能组合而存在,大家要清楚
9. 抽象类
Dart 的抽象类中的抽象方法不用写abstract
关键字,一般认为 Dart 的抽象类更像是接口,一般都是当接口用的
abstract class Cat{
void speak();
}
10. 接口
Dart 的接口实在是怪,Dart 中其实是没有接口这个概念的,在 Dart 里接口就是对象,我们用implements
关键字可以直接继承一个对象,但是该对象中的所有属性和方法都需要我们重写
class Animal {
var name, sex;
Animal(this.name);
Animal.withSex(this.sex);
}
class Dog implements Animal {
@override
var name;
@override
var sex;
}
所以这里就体现出上面说的抽象类的重要性了,Dart 里我们一般把接口写成抽象类
abstract class Cat{
void speak();
}
class Dog implements Cat {
@override
void speak() {
// TODO: implement speak
}
}
11. Mixin 混入
Dart 的 implements 限制不小,比如你继承一个 dart 中的 接口
,你必须重写这个接口里面所有的属性和方法,不管这个方法是不是抽象的
abstract class A {
runA();
speakA() {}
}
abstract class B {
runB();
}
class AA implements A, B {
@override
runA() {
// TODO: implement runA
return null;
}
@override
runB() {
// TODO: implement runB
return null;
}
@override
speakA() {
// TODO: implement speakA
return null;
}
}
这样明显限制太大,很多时候用着并不舒服,所以 Dart 提供一种新的接口类型:Mixin
,你实现 Mixin
类型的接口后,这样你就可以像 java 那样去实现接口了,注意实现接口时要用:with
mixin A {
runA();
speakA() {}
}
mixin B {
runB();
}
class AA with A, B {
@override
runA() {
// TODO: implement runA
return null;
}
@override
runB() {
// TODO: implement runB
return null;
}
}
12. 重写操作符
这是个骚操作,会让我们的代码看着非常高大上,什么是操作符:+,-,++,>
这些就是,我们在class内部使用operator
关键字+操作符号就可以给我们的对象添加诸如+,-,++,>
这样的操作了
class Book{
var price;
var _name = "AA";
Book(this.price);
operator >( Book otherBook ){
return this.price > otherBook.price;
}
operator [](String attribute){
switch(attribute){
case "price":{return price;}break;
case "name":{return _name;}break;
default: {return null;}
}
}
}
void main() {
var book1 = Book(20);
var book2 = Book(30);
print( book1 > book2 );
print( book1["price"] );
print( book1["name"] );
}
book1["price"]
这可以让我们换个方式调用对象的属性,装逼用的,大家看上面的写法就成,用这个方法可以在外面拿到对象的私有属性
泛型
Dart 中的泛型和 JAVA 语法差不多
1. 类中使用泛型
class Location {
Object x;
Object y;
Location(this.x, this.y);
}
2. 方法中使用泛型
class Location<T> {
T x;
T y;
Location(this.x, this.y);
}
3. 集合中使用泛型
- 3.1 list
// 不使用泛型限制数据类型
var names = ['aa', 'bb', 'cc', 111];
// 使用泛型之后
var names = <String>['aa', 'bb', 'cc'];
- 3.2 map
// 不使用泛型限制数据类型
var infos = {1: 'one', 'name': 'why', 'age': 18};
// 使用泛型之后
Map<String, String> infos = {'name': 'why', 'age': '18'};
库的导入
前面简单的说了下导库,这里咱们详细来说说
1. Dart 导库的3种方式:
-
Dart 标准库
- 这里库都是 Dart 内部自带的,比如:dart:io、dart:html、dart:math
import 'dart:io';
-
Pub 包管理器
- 使用前缀:package:
,像 Flutter 提供的包就是用 Pub 管理器导入
import 'package:flutter/material.dart';
-
指定路径
- 可以是相对路径或绝对路径
import 'lib/student/student.dart';
2. show、hide、as 关键字
-
show
- 只导入指定的文件 -
hide
- 隐藏指定文件,其他都导入 -
as
- 起别名
import 'lib/student/student.dart' show Student, Person;
import 'lib/student/student.dart' hide Person;
import 'lib/student/student.dart' as Stu;
// 使用的使用要加上别名
Stu.Student s = new Stu.Student();
这点其实和 python 是一模一样的
3. library、part 定义库
我们可以用 library
给库起个名字,没名字的库你也没法用不是,然后当库很大时,一个文件装不下时 part
就起作用了, 使用 part
可以把子文件关联进库主文件中,库打包的时候一起都打进去,不会造成文件缺失
使用思路:
- 子文件使用
part of "库名"
的方式关联库主文件 - 库主文件使用
子文件名.dart
的方式把子文件绑定进库里
主文件 AB.dart
library AB
part "mathUtils.dart";
part "dateUtils.dart";
子文件 mathUtils.dart
part of "AB.dart";
int sum(int num1, int num2) {
return num1 + num2;
}
恩,有些麻烦,需要子文件和主文件都相互声明,有点绕,初上手会很困惑的,好在一切不合理的都会被消灭,这东西现在过时啦
4. export 定义库
export
就是用来代替 path
的,在 export
的时代,我们只需要在库主文件声明一下需要的子文件就行啦,不用再折腾子文件了
主文件 AB.dart
library AB;
export "mathUtils.dart";
export "dateUtils.dart";
子文件 mathUtils.dart
int sum(int num1, int num2) {
return num1 + num2;
}