Dart入门(三)类、泛型、异常处理
一、类
1、定义一个类
class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } }
简化后可写成
class Person { String name; int age; // 在构造方法中初始化成员变量时,可使用如下写法简化 Person(this.name, this.age); }
如需处理其他变量时,也可单独对其操作
class Person { String name; int age;
// 如需处理其他变量时,也可单独对其操作 Person(this.name, this.age, String address){ print(address); } }
2、Getters 和 Setters方法
所有类中都包含隐式的getter方法,对于非final
修饰的成员,类中还包含隐式的setter方法。
很多时候我们调用setter和getter方法并不仅仅是为了赋值和访问,而是为了一些额外的处理
class Person { String userName; Person(this.userName); // 方法名前加get关键字 String get name{ return "user:" + this.userName; } // 方法名前加set关键字 set name(String name){ // do something this.userName = name; } }
可以通过实现 getters 和 setters 来创建附加属性,也就是直接使用 get 和 set 关键词:
class Rectangle { num left; num top; num width; num height; Rectangle(this.left, this.top, this.width, this.height); // 定义两个计算属性: 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; } main() { var rect = new Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
3、命名构造方法
不支持构造方法重载,但有命名构造方法
class Person { String userName; int age; Person(this.userName, this.age); // 命名构造方法 Person.fromData(Map data) { this.userName = data['name']; this.age = data['age']; } } void main() { // 使用命名构造方法创建对象 var p = new Person.fromData({ "name":"Bob", "age":19 }); print(p.userName); }
子类只能继承父类无名、无参数的构造函数(即子类不会继承父类的无名有参构造函数和命名构造函数)
默认情况下,子类只能调用父类的无名、无参数的构造函数,
如果父类不显示提供无名无参的构造函数,在子类中必须手动调用父类的一个构造函数(这里应该是这父类的一个有名构造函数)。
lyh总结:子类构造函数需要手动调用父类的一个构造函数(若父类有一个显示的无名无参构造函数,子类可不用手动调父类的构造函数(默认调显示的无名无无参构造函数了)。)
4、常量构造函数
如果想提供一个状态永远不变的对像,在Dart中,我们可以创建一个编译时常量对象,节省开销。
如果类提供的是状态不变的对象,那么可以把这些对象定义为编译时常量
,实现这种功能可以定义const
构造函数,且声明所有类的变量为final
,如:
class ConstPoint { final num x; final num y; // 使用const修构造方法 const ConstPoint(this.x, this.y); // 编译时常量对象,需使用const来创建对象 // 常量如果是类级别的,请使用 static static final ConstPoint origin = const ConstPoint(0, 0); } void main() { print(ConstPoint.origin.x); print(ConstPoint.origin.y); }
5、静态方法、静态常量
static 关键字来实现类级别的变量和函数、类中的常量需要使用static const声明
静态成员不能访问非静态成员、非静态成员可以访问静态成员
class People{ static int age; static void Say(){ print('hello!'); } } void main(){ People.age = 10; People.Say(); }
6、工厂构造方法
当我们需要创建一个新的对象或者从缓存中取一个对象时,工厂构造方法就派上了用场。
注意:工厂构造函数不能用 this。
class Logger { final String name; // 创建一个静态Map做为缓存, static final Map<String, Logger> _cache = <String, Logger>{}; // 定义一个命名构造方法,用下划线"_"修饰,将构造方法私有化 Logger._internal(this.name); // 使用关键字factory修饰类同名构造方法 factory Logger(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { // 调用命名构造方法创建新对象 final logger= new Logger._internal(name); _cache[name] = logger; // 存入缓存 return logger; } } } void main() { var uiLog = new Logger('UI'); var eventLog = new Logger('event'); }
7、构造方法重定向
有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向,
class Point { num x; num y; // 同名构造方法 Point(this.x, this.y); // 命名构造方法重定向到同名构造方法,中间使用一个冒号 Point.alongXAxis(num x) : this(x, 0); }
8、类的初始化列表
位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号
逗号分隔的一些赋值语句组成
适合用来初始化 final
修饰的变量
调用是在构造方法之前
class Point { final num x; final num y; final num distance; Point(x, y) : x = x, y = y, distance = sqrt(x * x + y * y){ print("这是构造方法"); } } void main() { var p = new Point(2, 3); print(p.distance); }
9、运算符重写
class Point { int x; int y; Point(this.x, this.y); // 使用operator关键字,为该类重载"+"运算符 Point operator +(Point p) { return new Point(this.x + p.x, this.y + p.y); } // 为该类重载"-"运算符 Point operator -(Point p) { return new Point(this.x - p.x, this.y - p.y); } } void main(){ var p1 = new Point(1,5); var p2 = new Point(7,10); // 重载运算符后,类可以使用“+”、“-” 运算符操作 var p3 = p1 + p2; var p4 = p2 - p1; print("${p3.x}, ${p3.y}"); print("${p4.x}, ${p4.y}"); }
10、类的单继承
class Father { myFunction(){ // do something } } class Son extends Father { @override myFunction(){ super.myFunction(); // do something } }
11、mixin 混入
当我们的继承父类不是同一个的,同时子类里面需要实现同样的功能时,Mixin显得尤为重要。
demo1:
abstract class Animal {} abstract class Mammal extends Animal {} abstract class Bird extends Animal {} abstract class Fish extends Animal {} abstract class Walker { // This class is intended to be used as a mixin, and should not be // extended directly. factory Walker._() => null; void walk() { print("I'm walking"); } } abstract class Swimmer { // This class is intended to be used as a mixin, and should not be // extended directly. factory Swimmer._() => null; void swim() { print("I'm swimming"); } } abstract class Flyer { // This class is intended to be used as a mixin, and should not be // extended directly. factory Flyer._() => null; void fly() { print("I'm flying"); } } class Dolphin extends Mammal with Swimmer {} class Bat extends Mammal with Walker, Flyer {} class Cat extends Mammal with Walker {} class Dove extends Bird with Walker, Flyer {} class Duck extends Bird with Walker, Swimmer, Flyer {} class Shark extends Fish with Swimmer {} class FlyingFish extends Fish with Swimmer, Flyer {}
demo2:
class Father1 { a(){ print("this is a func"); } common(){ print("common Father1"); } } class Father2 { b(){ print("this is b func"); } common(){ print("common Father2"); } } class Father3 { c(){ print("this is c func"); } common(){ print("common Father3"); } } //定义子类 class Son extends Father1 with Father2,Father3{ } void main() { var obj = new Son(); obj.common(); obj.a(); obj.b(); obj.c(); }
打印结果
common Father3 this is a func this is b func this is c func
以上写法中,也可以直接使用with
,等价于如下写法
class Son with Father1,Father2,Father3{ }
ps:
1、另外还需要注意一点,Dart中没有构造方法的重载,不能写两个同名的构造方法。
2、子类
会继承父类显示的
的无名无参的构造函数,父类中有命名构造函数,子类要手动继承。
3、@override (重写)
父类中有方法func(),子类会继承,子类想重写func(),会自动加上@override标记,且在方法中会自动加上super.func();
如果删掉super.func(),则父亲类中的func()不会被调用,
另外,子类中没实现func(),则子类调用func(),父类func()会被调用。
- initializer list(初始化列表)
- super class’s no-arg constructor(父类无参数构造函数)
- main class’s no-arg constructor (主类无参数构造函数)
对于final
变量的值,可以在初始化列表中指定
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父类没有无参数的非命名构造函数,必须手动调用一个构造函数
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
二、抽象类
Dart语言没有提供interface
关键字来定义接口,
但是Dart语言中保留了抽象类,同Java,使用abstract
关键字来修饰抽象类。而Dart中的抽象类,实际上就相当于Java中的接口。
抽象类是不能被实例化的,子类继承抽象类时,必须实现全部抽象方法。
如果你想让你的抽象类被实例化,请定义一个 工厂构造函数 。
abstract class Base { // 省略函数体即可定义抽象方法,不需加关键字 func1(); func2(); }
三、隐式接口
实际上在Dart中,每个类都隐式的定义了一个包含所有实例成员的接口, 并且该类实现了这个接口
因此,如果我们想实现某个接口,但有又不想继承,则可以使用这种隐式接口机制。我们需要用到关键字implements
class People { void greet(){ print("Hello"); } } class Student implements People{ @override void greet(){ print("Hi,I'm Alice."); } } greet(People p){ p.greet(); } void main() { greet(new Student()); }
接口是可以多实现
的,如下:
class TonyStart implements American, Scientist, Richman, Playboy { // ... }
四、泛型
var names = new List<String>(); names.add("zhangsan") var maps = new Map<int, String>(); maps[1]="value";
字面量写法
var infos = <String>['Seth', 'Kathy', 'Lars']; var pages = <String, String>{ 'index.html': 'Homepage', 'robots.txt': 'Hints for web robots' };
五、异常处理
try { // 使除数为0 print(11~/0); } on IntegerDivisionByZeroException { print("除数为0"); }on Exception{ print("Exception"); }finally { print("finally"); }
不关心具体异常,只想捕获,避免异常继续传递
try { print(11~/0); } catch(e){ // 打印报错信息 print(e); }finally { print("finally"); }
如果想获取更多异常信息,可以使用两个参数的catch
,第二个参数是异常的调用栈信息
try { print(11~/0); } catch(e,s){ print(s); }
如果你既想针对不同异常进行不同处理,还想打印调用栈信息,那就将两种结合起来使用
try { print(11~/0); } on IntegerDivisionByZeroException catch(e,s){ print(s); } on Exception catch(e,s){ print(s); }