異常
Dart代碼可以拋出并捕獲異常。如果未捕獲異常,則會引發(fā)程序終止
與Java相比懦窘,Dart的所有異常都是未經(jīng)檢查的異常。方法不會聲明它們可能引發(fā)的異常稚配,并且您不需要捕獲任何異常畅涂。
Dart提供了Exception和Error類型,以及許多預(yù)定義的子類型道川。當(dāng)然午衰,您可以定義自己的異常。Dart可以拋出任何非null
異常冒萄。
拋出異常
以下是拋出或引發(fā)異常的示例:
throw FormatException('這是一個異常');
你也可以拋出任意對象:
throw 'Out of llamas!';
注意:應(yīng)該盡可能的拋出錯誤或異常的類型臊岸。
因為拋出異常是一個表達(dá)式,所以可以在=>
語句中以及允許表達(dá)式的任何其他地方拋出異常:
void distanceTo(Point other)=> throw UnimplementedError();
捕獲
捕獲異常會阻止異常傳遞(除非您重新拋出異常)尊流。捕獲異常使您有機(jī)會處理它:
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()指定一個或兩個參數(shù)忿晕。第一個是拋出的異常装诡,第二個是堆棧跟蹤(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
關(guān)鍵字:
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
無論是否拋出異常鸦采,要確保某些代碼運(yùn)行,請使用finally
子句咕幻。 如果沒有catch子句與異常匹配渔伯,則在finally子句運(yùn)行后傳遞異常:
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.
}
類
獲取對象的類型
要在運(yùn)行時獲取對象的類型,可以使用Object的runtimeType
屬性肄程,該屬性返回Type
對象锣吼。
print('a type is ${a.runtimeType}');
以下是聲明實例變量的方法:
class Point {
num x; //聲明實例變量x,最初為null蓝厌。
數(shù)字; //聲明y玄叠,最初為null。
num z = 0; //聲明z拓提,最初為0读恃。
}
所有未初始化的實例變量的值都為null
。
所有實例變量都生成一個隱式getter
方法。非最終實例變量也會生成隱式setter
方法寺惫。
構(gòu)造函數(shù) :
通過創(chuàng)建與其類同名的函數(shù)來聲明構(gòu)造函數(shù)(另外疹吃,可選地,如命名構(gòu)造函數(shù)中所述的附加標(biāo)識符)肌蜻。最常見的構(gòu)造函數(shù)形式,即生成構(gòu)造函數(shù)必尼,創(chuàng)建一個類的新實例:
class Point {
num x蒋搜,y;
Point(num x,num y){
this.x = x;
this.y = y;
}
}
this關(guān)鍵字引用當(dāng)前實例判莉。
將構(gòu)造函數(shù)參數(shù)賦值給實例變量的模式是如此常見豆挽,Dart具有語法糖,使其變得簡單:
class Point {
num x券盅,y;
//用于設(shè)置x和y的語法糖
//在構(gòu)造函數(shù)體運(yùn)行之前
Point(this.x帮哈,this.y);
}
默認(rèn)構(gòu)造函數(shù)
如果您未聲明構(gòu)造函數(shù),則會為您提供默認(rèn)構(gòu)造函數(shù)锰镀。默認(rèn)構(gòu)造函數(shù)沒有參數(shù)娘侍,并在超類中調(diào)用無參數(shù)構(gòu)造函數(shù)。
構(gòu)造函數(shù)不是繼承的
子類不從其超類繼承構(gòu)造函數(shù)泳炉。聲明沒有構(gòu)造函數(shù)的子類只有默認(rèn)(無參數(shù)憾筏,無名稱)構(gòu)造函數(shù)。
命名構(gòu)造函數(shù)
使用命名構(gòu)造函數(shù)為類實現(xiàn)多個構(gòu)造函數(shù)或提供額外的清晰度:
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
請記住花鹅,構(gòu)造函數(shù)是不繼承的氧腰,這意味著超類的命名構(gòu)造函數(shù)不會被子類繼承。如果希望使用超類中定義的命名構(gòu)造函數(shù)創(chuàng)建子類刨肃,則必須在子類中實現(xiàn)該構(gòu)造函數(shù)古拴。
調(diào)用非默認(rèn)的超類構(gòu)造函數(shù)
默認(rèn)情況下,子類中的構(gòu)造函數(shù)調(diào)用超類的未命名的無參數(shù)構(gòu)造函數(shù)真友。超類的構(gòu)造函數(shù)在構(gòu)造函數(shù)體的開頭被調(diào)用黄痪。如果還使用初始化列表,則在調(diào)用超類之前執(zhí)行盔然÷Γ總之,執(zhí)行順序如下:
- 初始化列表
- 超類的無參數(shù)構(gòu)造函數(shù)
- 主類的無參數(shù)構(gòu)造函數(shù)
如果超類沒有未命名的無參數(shù)構(gòu)造函數(shù)轻纪,則必須手動調(diào)用超類中的一個構(gòu)造函數(shù)油额。在冒號(:)之后指定超類構(gòu)造函數(shù),就在構(gòu)造函數(shù)體(如果有)之前刻帚。
常量構(gòu)造函數(shù)
如果您的類生成永遠(yuǎn)不會更改的對象潦嘶,則可以使這些對象成為編譯時常量。為此崇众,請定義const構(gòu)造函數(shù)并確保所有實例變量都是final:
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工廠建設(shè)者
在實現(xiàn)并不總是創(chuàng)建其類的新實例的構(gòu)造函數(shù)時掂僵,請使用factory
關(guān)鍵字航厚。例如,工廠構(gòu)造函數(shù)可能從緩存中返回實例锰蓬,或者它可能返回子類型的實例幔睬。
以下示例演示了從緩存中返回對象的工廠構(gòu)造函數(shù):
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);
}
}
注意:工廠構(gòu)造函數(shù)無權(quán)訪問this
。
像調(diào)用任何其他構(gòu)造函數(shù)一樣調(diào)用工廠構(gòu)造函數(shù):
var logger = Logger('UI');
logger.log('Button clicked');
getters and setters
getter
和setter
是提供對象屬性的讀寫訪問權(quán)限的特殊方法芹扭÷槎ィ回想一下,每個實例變量都有一個隱式getter
舱卡,如果合適的話還有一個setter
辅肾。您可以使用get
和set
關(guān)鍵字通過實現(xiàn)getter
和setter
來創(chuàng)建其他屬性:
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方法可以是抽象的轮锥,定義一個接口矫钓,但將其實現(xiàn)留給其他類。 抽象方法只能存在于抽象類中舍杜。
要使方法成為抽象新娜,請使用分號(;)
而不是方法體:
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()));
}
這是一個指定類實現(xiàn)多個接口的示例:
class Point implements Comparable, Location {...}
繼承extending
使用extends
創(chuàng)建子類,使用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() {...}
}
重載運(yùn)算符
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));
}
注意:如果覆蓋
==
,則還應(yīng)該覆蓋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是一種在多個類層次結(jié)構(gòu)中重用類代碼的方法。
要使用mixin颜矿,請使用with
關(guān)鍵字后跟一個或多個mixin名稱寄猩。以下示例顯示了兩個使用mixins的類:
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要實現(xiàn)mixin,請創(chuàng)建一個擴(kuò)展Object
的類骑疆,并且不聲明構(gòu)造函數(shù)田篇。除非您希望mixin可用作常規(guī)類,否則請使用mixin
關(guān)鍵字而不是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可以調(diào)用它沒有定義的方法 , 使用on
來指定所需的超類:
mixin MusicalPerformer on Musician {
// ···
}