Dart语言 4 异常和类

异常

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
gettersetter是提供对象属性的读写访问权限的特殊方法。回想一下,每个实例变量都有一个隐式getter,如果合适的话还有一个setter。您可以使用getset关键字通过实现gettersetter来创建其他属性:

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();
  }
}

子类可以覆盖实例方法,gettersetter。 您可以使用@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));
}

注意:如果覆盖==,则还应该覆盖ObjecthashCode 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 {
  // ···
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容