作者:opLW
目的:在阅读Dart概览后进行的总结,主要记录Dart相比Java的新特性,包括许多亲自实验后的总结。如有错误还望指正😄
其他文章:
Dart – 较Java新特性(一)Dart – 较Java新特性(二)
目录
8.异常
9.类
8.异常
- 8.1 概述
- 与Java不同Dart中没有必检异常和非必检异常之分,Dart中只有非必检异常。
- 即Dart不要求在一个方法中声明抛出异常(即没有和Java一样的关键字
throws
)- Dart不会要求必须使用
try..catch
结构捕获异常。
void main() {
throw "I am a String";
}
// 抛出异常:Uncaught Error: I am a String
- 8.2 throw 与
Java
类似用于抛出一个异常。 - 8.3 catch
- on、catch 与Java不同Dart将声明异常类型的部分分离出来,用
on
关键字声明,用catch
关键字捕获异常。
try {
// 发生异常的代码
} on Exception catch (e) {
// 用on声明异常的类型
print('Unknown exception: $e');
} catch (e) {
// 没有用on声明异常类型,所以对于任何异常都能处理
print('Something really unknown: $e');
}
catch
块可以指定两个参数:第一个为e
,代表异常;第二个为s
,代表StackTrace
异常的栈信息。
try {
throw "null null";
} catch(e, s) {
print("$s");
}
// 同时如果你不喜欢e和s,也可以换成别的关键字如:
try {
throw "null null";
} catch(a, b) {
print("$b");
}
- rethrow
rethrow
关键字可以将异常重新抛出,所以我们不仅可以在发生异常时处理异常,也可以将异常抛出反馈给函数调用者。
void fun() {
try {
throw "null null";
} catch(e, s) {
// 对异常进行预处理
rethrow;
}
}
void main() {
try {
fun()
} catch(e) {
// 函数调用者进行处理
}
}
- 8.4 finally 与
Java
类似。
9.类
- 9.1 构造函数
- 9.1.1 简化的构造函数 如果不在函数体中做特别的事情,可将函数简化为:
// 此时必须有“;”
Bicycle(this.cadence, this.speed, this.gear);
// 等价于
Bicycle(int cadence, int speed, int gear) {
this.cadence = cadence;
this.speed = speed;
this.gear = gear;
}
- 9.1.2 默认构造函数 与java一样,Dart会提供一个默认的无参构造函数,在默认构造函数内会默认调用父类的无参构造函数。
- 9.1.3 重写构造函数 与java类似,Dart也可以创建自己的构造函数。与java一样,自定义构造函数之后,会失去默认的无参构造函数。
void main() {
// var a1 = A(); 发生错误,因为无参构造函数被覆盖了
var a2 = A(1);
}
class A {
var i;
A(this.i);
}
- 9.1.4 构造函数不能被继承 与java一样,构造函数不能被子类继承。
- 9.1.5 命名构造函数 除了常规的构造函数外,Dart引入了命名构造函数。我们可能因为不同的目的创建一个类,为构造函数命名有利于更好的识别不同的目的。形式如
类名.命名构造函数名字(){方法体}
,具体如下:
void main() {
var p1 = Point(1, 2);
var p2 = Point.makeFromOtherPoint(p1); // 使用命名构造函数
}
class Point {
num x, y;
Point(this.x, this.y);
// 声明命名构造函数
Point.makeFromOtherPoint(Point other) {
x = other.x;
y = other.y;
}
}
- 9.1.6 初始化列表 Java会在调用构造函数前,对成员变量进行初始化。同样的Dart中通过初始化列表在构造函数调用前进行初始化。
class Point {
final num x;
final num y;
final num distanceFromOrigin;
// 初始化例表在构造方法签名后,方法体前
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
- 注意
- 开发时,可以在初始化列表中使用
assert
进行判断- 初始化列表中,只能使用静态方法
- 9.1.7 调用父类构造函数 都知道无参的默认构造函数会默认先调用父类的无参构造函数。如果想调用父类的有参构造函数,需在子类的构造函数末尾添加
: super(参数)
,具体如下:
class A {
var a = 1;
A(this.a);
}
class B extends A {
final c;
var d = 4;
// 调用父类的有参构造函数
B() : super(1) {}
// 初始化列表和super结合时,super部分应该放在末尾
B(int p1)
: c = 3,
d = 4,
super(p1) {}
}
- 注意
- 当初始化列表存在和调用父类的构造函数同时存在时,会优先执行初始化列表 > 父类构造函数 > 子类构造函数。虽然说Dart初始化列表类似Java中对成员变量进行初始化,但在java中父类的构造函数会先于子类的成员变量初始化,而Dart则相反子类的初始化列表先执行。
- 在
super()
的方法体中允许调用函数,但该函数需为静态函数或者其他非本类和非父类的实例函数,同时不能使用this
。
- 9.1.8 调用本类的其他构造函数
class Point {
num x, y;
// 类的主构造函数。
Point(this.x, this.y);
// 指向主构造函数
Point.alongXAxis(num x) : this(x, 0);
}
- 9.1.9 常量构造函数 如果一个类的所有对象总是固定不变的,那么可以将这些对象声明为编译期常量。声明为编译期常量,需要将所有属性声明为常量并且声明一个常量构造函数。
class ImmutablePoint {
final num x, y;
const ImmutablePoint(this.x, this.y);
}
- 注意 上述步骤只是让一个类具备了成为编译期常量的能力,然而只有正确使用这个能力才能创建编译期常量。如下代码:
// 方法一:在实例化对象前添加const
var a = const ImmutablePoint(1, 1); // 创建一个常量对象
var b = ImmutablePoint(1, 1); // 创建一个非常量对象
assert(!identical(a, b)); // 两者不是同一个实例!
// 方法二:创建一个常量上下文,此后在常量上下文内创建的对象都为常量(前提是改类型含有常量构造函数)
const pointsMap = {
'point1': ImmutablePoint(0, 0),
'point2': ImmutablePoint(1, 10)
};
- 9.1.10 工厂构造函数 传统的构造函数都是创建一个新的对象,工厂构造函数让构造函数不再总是创建新对象。工厂构造函数的调用与正常构造函数一样。
class Logger {
final String name;
// _cache 是私有属性。
static final Map<String, Logger> _cache =
<String, Logger>{};
Logger(this.name);
factory Logger(String name) {
if (_cache.containsKey(name)) {
// 从缓存中获取
return _cache[name];
} else {
// 调用正常的构造函数创建对象并设置到缓存中
final logger = Logger(name);
_cache[name] = logger;
return logger;
}
}
}
- 注意 由于工厂构造函数并不总是创建新的对象,所以无法在工厂构造函数内使用
this
。
- 9.2 getter和setter
- 9.2.1 默认getter和setter Dart默认为所有变量提供默认
getter
以及为非常量提供setter
方法。
class Point {
num x;
}
void main() {
var point = Point();
point.x = 4; // 实际上调用setter方法进行设置
}
- 9.2.2 重载getter和setter方法 通过自定义
getter
和setter
添加自己的内容。详细注意点见:
class Point {
// 不需要声明变量x
num get x {
print("access val x");
return x;
}
set x(num value) {
print("the x is $value now");
}
num get y {
//print("$y"); 错误:访问y,导致循环调用y.get()方法,导致栈溢出
return y;
}
set y(num value) {
//y = value; 错误:设置y,引发栈溢出
print("$value"); // 只能通过value访问
}
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
}
- 注意
- 重写的
getter
和setter
方法具有同时声明变量的能力,不需要额外声明变量getter
方法内,除了return
语句外,其他地方不能访问该变量setter
方法内,不需要额外使用如y = value
的赋值语句,同时只能通过value
访问该变量- 如自减、自增等需要多次访问变量的运算符,Dart只会调用一次
getter
,然后用临时变量暂存避免重复调用引发错误
- 9.3 抽象类 与Java基本相同。
- 9.4 隐式接口 Dart中不再显示的提供
interface
用于声明接口,而是为每一个类隐式的生成接口。即我们可以实现一个普通的类,此时会要求实现该类的所有实例方法(不包括静态方法和构造方法)、getter
和setter
及该类实现的接口的方法。
class A {
void aFun() {
print("I am aFun in A Class.");
}
}
class B implements A {
fianl b1 = 1;
var b2 = 2;
}
class C implements B {
// 此时会要求实现A.aFun()、B.b1的getter方法、B.b2的getter和setter
}
- 9.5 继承 与Java基本相同。
- 重写方法 通过
@override
关键字标记重写方法。 - 重写运算符 Dart允许对运算符进行重载,这样就可以让两个对象相加了。
- 允许重写的运算符
<
、<=
、>
、>=
、+
、-
、/
、~/
、*
、%
、|
、^
、&
、<<
、>>
、[]
(下标访问符)、[]=
(下标赋值符)、~
、==
- 重写运算符的函数模式 如:
返回值 operator 运算符(参数) {方法体}
总体上多了关键字operator
并把方法名改为对应要重写的符号
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
}
- 重写== Dart的
==
类似于Java中的equals
方法,所以相同的重写==
符号时,需要重写hashCode
的getter
方法。
- 9.6 快速扩展类的功能–Mixin
- 参考文章 混入–Mixin
- Java中实现接口存在的问题 Java中如果几个类具有相同的功能,我们往往将这些共同的功能抽象为接口,这样有什么问题?Java中往往会出现一种情况就是不同的类需要实现同一个接口,并且实现的代码是一样的,如狗和鸟都需要实现走路这个方法,这样就带来了重复性工作;同时鸟除了走路之外还会飞,所以还需要另外的接口。因此就有较多的接口,较多的方法需要实现
- Dart的Mixin能解决的问题 Dart中通过Mixin,使得一个类如果需要某个方法或属性,只需简单的通过
with TargetClass
来快速获取该类的方法。如下代码所示,此时P拥有A的fun1
方法,不需要在P中再实现。
class A {
void fun1();
}
class P with A
- 注意点 假设Mixin的多个类中含有相同的方法,则后者会覆盖前者对该方法的实现。
mixin A {
void fun1();
}
mixin B {
void fun1();
}
class P with A, B // A和B具有相同的方法最后P得到的fun1为B中的实现
tip 可通过将A和B的class关键字换为mixin来让A和B只能作为被混入的对象,而不能作为被继承的类
- 9.7 枚举类 和Java一样,Dart的枚举类不能被继承、实例化。
- 9.8 获取实例对象的类型 所有类的父类Object提供了一个属性
runtimeType
用于获取实例的类型。
print('The type of a is ${a.runtimeType}');
万水千山总是情,麻烦手下别留情。
如若讲得有不妥,文末留言告知我,
如若觉得还可以,收藏点赞要一起。