异常
Dart代码可以抛出并捕获异常。如果未捕获异常,则会引发程序终止
与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。
Dart提供了Exception和Error类型,以及许多预定义的子类型。当然,您可以定义自己的异常。Dart可以抛出任何非null
异常。
抛出异常
以下是抛出或引发异常的示例:
throw FormatException('这是一个异常');
你也可以抛出任意对象:
throw 'Out of llamas!';
注意:应该尽可能的抛出错误或异常的类型。
因为抛出异常是一个表达式,所以可以在=>
语句中以及允许表达式的任何其他地方抛出异常:
void distanceTo(Point other)=> throw UnimplementedError();
捕获
捕获异常会阻止异常传递(除非您重新抛出异常)。捕获异常使您有机会处理它:
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
如前面的代码所示,您可以使用on或catch或两者。需要指定异常类型时使用。在异常处理程序需要异常对象时使用catch。
您可以为catch()指定一个或两个参数。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象):
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
要部分处理异常,同时允许它传播,请使用rethrow
关键字:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
Finally
无论是否抛出异常,要确保某些代码运行,请使用finally
子句。 如果没有catch子句与异常匹配,则在finally子句运行后传递异常:
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
The finally clause runs after any matching catch clauses:
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
类
获取对象的类型
要在运行时获取对象的类型,可以使用Object的runtimeType
属性,该属性返回Type
对象。
print('a type is ${a.runtimeType}');
以下是声明实例变量的方法:
class Point {
num x; //声明实例变量x,最初为null。
数字; //声明y,最初为null。
num z = 0; //声明z,最初为0。
}
所有未初始化的实例变量的值都为null
。
所有实例变量都生成一个隐式getter
方法。非最终实例变量也会生成隐式setter
方法。
构造函数 :
通过创建与其类同名的函数来声明构造函数(另外,可选地,如命名构造函数中所述的附加标识符)。最常见的构造函数形式,即生成构造函数,创建一个类的新实例:
class Point {
num x,y;
Point(num x,num y){
this.x = x;
this.y = y;
}
}
this关键字引用当前实例。
将构造函数参数赋值给实例变量的模式是如此常见,Dart具有语法糖,使其变得简单:
class Point {
num x,y;
//用于设置x和y的语法糖
//在构造函数体运行之前
Point(this.x,this.y);
}
默认构造函数
如果您未声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。
构造函数不是继承的
子类不从其超类继承构造函数。声明没有构造函数的子类只有默认(无参数,无名称)构造函数。
命名构造函数
使用命名构造函数为类实现多个构造函数或提供额外的清晰度:
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
请记住,构造函数是不继承的,这意味着超类的命名构造函数不会被子类继承。如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。
调用非默认的超类构造函数
默认情况下,子类中的构造函数调用超类的未命名的无参数构造函数。超类的构造函数在构造函数体的开头被调用。如果还使用初始化列表,则在调用超类之前执行。总之,执行顺序如下:
- 初始化列表
- 超类的无参数构造函数
- 主类的无参数构造函数
如果超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。在冒号(:)之后指定超类构造函数,就在构造函数体(如果有)之前。
常量构造函数
如果您的类生成永远不会更改的对象,则可以使这些对象成为编译时常量。为此,请定义const构造函数并确保所有实例变量都是final:
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工厂建设者
在实现并不总是创建其类的新实例的构造函数时,请使用factory
关键字。例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例。
以下示例演示了从缓存中返回对象的工厂构造函数:
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
注意:工厂构造函数无权访问this
。
像调用任何其他构造函数一样调用工厂构造函数:
var logger = Logger('UI');
logger.log('Button clicked');
getters and setters
getter
和setter
是提供对象属性的读写访问权限的特殊方法。回想一下,每个实例变量都有一个隐式getter
,如果合适的话还有一个setter
。您可以使用get
和set
关键字通过实现getter
和setter
来创建其他属性:
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
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);
}
抽象方法
实例、getter和setter方法可以是抽象的,定义一个接口,但将其实现留给其他类。 抽象方法只能存在于抽象类中。
要使方法成为抽象,请使用分号(;)
而不是方法体:
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}
抽象类
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
这是一个指定类实现多个接口的示例:
class Point implements Comparable, Location {...}
继承extending
使用extends
创建子类,使用super
来引用超类:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
}
子类可以覆盖实例方法,getter
和setter
。 您可以使用@override
注释来指示您有意覆盖成员:
class SmartTelevision extends Television {
@override
void turnOn() {...}
}
重载运算符
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
// Operator == and hashCode not shown. For details, see note below.
// ···
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
注意:如果覆盖
==
,则还应该覆盖Object
的hashCode
getter方法。
枚举类型
enum Color { red, green, blue }
枚举中的每个值都有一个索引getter,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1:
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
mixins
Mixins是一种在多个类层次结构中重用类代码的方法。
要使用mixin,请使用with
关键字后跟一个或多个mixin名称。以下示例显示了两个使用mixins的类:
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要实现mixin,请创建一个扩展Object
的类,并且不声明构造函数。除非您希望mixin可用作常规类,否则请使用mixin
关键字而不是class
。
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
要指定只有某些类型可以使用mixin
- 例如,你的mixin可以调用它没有定义的方法 , 使用on
来指定所需的超类:
mixin MusicalPerformer on Musician {
// ···
}