Dart语言详解
变量-变量的声明
区别于java,新增 var dynamic Object
var:如果没有初始值,可以变成任何类型
dynamic:动态任意类型,编译阶段不检查类型
Object:动态任意类型,编译阶段检查检查类型
区别:
唯一区别 var 如果有初始值,类型被锁定,没有初始化的变量自动获取一个默认值为null,一切皆对象(此对象 非彼对象),对象的默认值为null
变量-final和const
共同点:
1.声明的类型可省略
2.初始化后不能再赋值
3.不能和var同时使用
区别:
1.类级别常量,使用static const
2.const可使用其他const 常量的值来初始化其值
3.使用const赋值声明,const可省略
4.可以更改非final、非const变量的值,即使曾经具有const值
5.const导致的不可变性是可传递的
6.相同的const常量不会在内存中重复创建
7.const需要是编译时常量
内置类型
Numbers 数值、Strings 字符串、Booleans 布尔值、Lists 列表(数组)、Sets 集合、Maps 集合、Runes 符号字符、Symbols 标识符、int : 整数值、double : 64-bit双精度浮点数、int和double是num的子类。
特殊讲解String:
Dart 字符串是 UTF-16 编码的字符序列,可以使用单引号或者双引号来创建字符串,可以使用三个单引号或者双引号创建多行字符串对象,可以使用 r 前缀创建”原始raw”字符串,可以在字符串中使用表达式: ${expression},如果表达式是一个标识符,可以省略 {},如果表达式的结果为一个对象,则 Dart 会调用对象的 toString() 函数来获取一个字符串
内置类型-List:
Dart中可以直接打印list包括list的元素,List也是对象。java中直接打印list结果是地址值
Dart中List的下标索引和java一样从0开始和java一样支持泛型。
有增删改查,支持倒序,自带排序、洗牌,可使用+将两个List合并
内置类型-Set:
set1.difference(set2):返回set1集合里有但set2里没有的元素集合
set1.intersection(set2):返回set1和set2的交集
set1.union(set2):返回set1和set2的并集
set1.retainAll():set1只保留某些元素(要保留的元素要在原set中存在)
内置类型-Runes:
Main(){
Runes runes = new Runes('\u{1f605} \u6211‘);
var str1 = String.fromCharCodes(runes);
print(str1);
}
Runes用于在字符串中表示Unicode字符。
使用String.fromCharCodes显示字符图形。
如果非4个数值,需要把编码值放到大括号中
函数
函数定义
1.可在函数内定义
2.定义函数时可省略类型
3.支持缩写语法 =>
Dart中函数是Function类型的对象。
所有的函数都返回一个值。如果没有指定返回值,则 默认把语句 return null; 作为函数的最后一个语句执行。
定义函数时可省略类型(不建议)。
对于只有一个表达式的方法,你可以选择 使用缩写语法=>表达式来定义。(Kotlin是用=来实现)
可在函数内部定义函数,支持嵌套。
函数-可选参数:
1.可选命名参数
2.可选位置参数
3.默认参数值
可选命名参数:使用 {param1, param2, …} 的形式来指定命名参数。
可选位置参数:把可选参数放到 [] 中,必填参数要放在可选参数前面。
可选命名参数默认值(默认值必须是编译时常量),可以使用等号‘=’或冒号’:‘。
Dart SDK 1.21 之前只能用冒号,冒号的支持以后会移除,所以建议使用等号。
可选位置参数默认值(默认值必须是编译时常量),只能使用等号'='。
可使用list或map作为默认值,但必须是const。
函数-匿名函数
1.可赋值给变量,通过变量调用
2.可在其他函数中直接调用或传递给其他函数
可以通过()调用,不推荐。
常用的List.forEach()就用的匿名函数。
函数-闭包
Function makeAddFunc(int x) {
x++;
return (int y) => x + y;
}
main() {
var addFunc2 = makeAddFunc(2);
var addFunc4 = makeAddFunc(4);
print(addFunc2(3));
print(addFunc4(3));
}
注:方法体只包含一个语句,可以使用”=>”缩写。
函数-函数别名
typedef Fun1(int a, int b);
typedef Fun2
int add(int a, int b) {
print('a + b');
return a + b;
}
class Demo1 {
Demo1(int f(int a, int b), int x, int y) {
var sum = f(x, y);
print("sum1 = $sum");
}
}
class Demo2 {
Demo2(Fun1 f, int x, int y) {
var sum = f(x, y);
print("sum2 = $sum");
}
}
class Demo3 {
Demo3(Fun2
var sum = f(x, y);
print("sum3 = $sum");
}
}
注:typedef给函数起一个别名,使用比较方便。例如定义一个方法的回调,直接使用别名定义。
没返回值,则只要参数匹配就行了,如果定义了返回值,则返回值不一样会报错。
操作符:
“?.”,条件成员访问 和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员。
“~/”,除后取整。
“as”,类型转换。
“is”,如果对象是指定类型返回true。
“is!”,如果对象是指定类型返回false。
“??”,双问号左边为true返回左边结果,否则返回右边结果。
“..”,级联语法。严格来说, 两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。
“??:”,如果左边是 null,则右边赋值给左边;如果不是 null,则左边的值保持不变
流程控制语句
if else
for, forEach, for-in
while , do-while
switch case
break , continue
Dart中控制流程语句和Java类似。
List和Set等实现了Iterable接口的类支持for-in遍历元素。
Dart 提供了 Exception 和 Error 类型, 以及一些子类型。还可以定义自己的异常类型。但是,Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
/ 抛出Exception 对象
// throw new FormatException(‘格式异常');
// 抛出Error 对象
// throw new OutOfMemoryError();
// 抛出任意非null对象
// throw '这是一个异常';
所有的 Dart 异常是非检查异常。 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。
Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
异常-捕获
try {
throw new OutOfMemoryError();
} on OutOfMemoryError {
print('没有内存了');
} on Error catch(e) {
print('Unknown error: $e');
} catch (e, s) {
print('Exception details: $e');
print('Stack Trace: $s');
} finally {
print('end');
}
所有的 Dart 异常是非检查异常。 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。
Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。
catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。
可以使用rethrow把捕获的异常重新抛出。
类-构造函数:
//java中写法
class Point {
double x;
double y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
//dart建议写法
class Point {
num x;
num y;
Point(this.x, this.y);
}
类-命名构造函数:
class Point {
num x;
num y;
Point(this.x, this.y);
//命名构造函数
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。
类-重定向构造函数:
class Point {
num x;
num y;
Point(this.x, this.y);
//重定向构造函数,使用冒号调用其他构造函数
Point.alongXAxis(num x) : this(x, 0);
}
一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
类-初始化列表:
import 'dart:math';
class Point {
//final变量不能被修改,必须被构造函数初始化
final num x;
final num y;
final num distanceFromOrigin;
//初始化列表
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。
初始化列表非常适合用来设置 final 变量的值。
类-调用超类构造函数:
eg:class Parent {
int x;
int y;
//父类命名构造函数不会传递
Parent.fromJson(x, y)
: x = x,
y = y {
print('父类命名构造函数');
}
}
class Child extends Parent {
int x;
int y;
//若超类没有默认构造函数, 需要手动调用超类其他构造函数
Child(x, y) : super.fromJson(x, y) {
//调用父类构造函数的参数无法访问 this
print('子类构造函数');
}
//在构造函数的初始化列表中使用super(),需要把它放到最后
Child.fromJson(x, y)
: x = x,
y = y,
super.fromJson(x, y) {
print('子类命名构造函数');
}
}
超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。
如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数。
调用超类构造函数的参数无法访问 this。
在构造函数的初始化列表中使用 super(),需要把它放到最后。
类-常量构造函数:
class Point2 {
//定义const构造函数要确保所有实例变量都是final
final num x;
final num y;
static final Point2 origin = const Point2(0, 0);
//const关键字放在构造函数名称之前,且不能有函数体
const Point2(this.x, this.y);
}
定义const构造函数要确保所有实例变量都是final。
const关键字放在构造函数名称之前。
类-工厂构造函数:
class Singleton {
String name;
//工厂构造函数无法访问this,所以这里要用static
static Singleton _cache;
//工厂方法构造函数,关键字factory
factory Singleton([String name = 'singleton']) =>
Singleton._cache ??= Singleton._newObject(name);
//定义一个命名构造函数用来生产实例
Singleton._newObject(this.name);
}
注:工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。
如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义这个构造函数。
工厂构造函数无法访问this。
类-Setter和Getter:
class Rectangle {
num left;
num top;
num width;
num 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;
}
每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。
可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter。
getter 和 setter 的好处是,你可以开始使用实例变量,后来 你可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。
类-抽象类:
1.abstract关键字修饰class
2,.继承的方式使用
3.接口的方式使用
不能被实例化,除非定义一个工厂构造函数。
抽象类通常用来定义接口, 以及部分实现。
抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。
接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。
一个类可以implement一个普通类。Dart任何一个类都是接口。
一个类可以implement多个接口。
类-可调用类:
class ClassFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var cf = new ClassFunction();
var out = cf("dongnao","flutter","damon");
print('$out');
print(cf.runtimeType);
print(out.runtimeType);
print(cf is Function);
}
实现call()方法可以让类像函数一样能够被调用。