🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 类(Classes) 1. ***对象*** * Dart 是一种面向对象的语言,并且支持基于mixin的继承方式。 * Dart 语言中所有的对象都是某一个类的实例,所有的类有同一个基类--Object。 * 基于mixin的继承方式具体是指:一个类可以继承自多个父类。 * 使用new语句来构造一个类,构造函数的名字可能是ClassName,也可以是ClassName.identifier, 例如: ~~~ var jsonData = JSON.decode('{"x":1, "y":2}'); // Create a Point using Point(). var p1 = new Point(2, 2); // Create a Point using Point.fromJson(). var p2 = new Point.fromJson(jsonData); ~~~ * 使用.(dot)来调用实例的变量或者方法。 ~~~ var p = new Point(2, 2); // Set the value of the instance variable y. p.y = 3; // Get the value of y. assert(p.y == 3); // Invoke distanceTo() on p. num distance = p.distanceTo(new Point(4, 4)); ~~~ * 使用`?.`来确认前操作数不为空, 常用来替代`.`, 避免左边操作数为null引发异常。 ~~~ ``` // If p is non-null, set its y value to 4. p?.y = 4; ``` ~~~ * 使用const替代new来创建编译时的常量构造函数。 ~~~ ``` var p = const ImmutablePoint(2, 2); ``` ~~~ * 使用runtimeType方法,在运行中获取对象的类型。该方法将返回Type 类型的变量。 ~~~ ``` print('The type of a is ${a.runtimeType}'); ``` ~~~ 2. ***实例化变量(Instance variables)*** * 在类定义中,所有没有初始化的变量都会被初始化为null。 ~~~ class Point { num x; // Declare instance variable x, initially null. num y; // Declare y, initially null. num z = 0; // Declare z, initially 0. } ~~~ * 类定义中所有的变量, Dart语言都会隐式的定义 setter 方法,针对非空的变量会额外增加 getter 方法。 ~~~ class Point { num x; num y; } main() { var point = new Point(); point.x = 4; // Use the setter method for x. assert(point.x == 4); // Use the getter method for x. assert(point.y == null); // Values default to null. } ~~~ 3. ***构造函数(Constructors)*** * 声明一个和类名相同的函数,来作为类的构造函数。 ~~~ class Point { num x; num y; Point(num x, num y) { // There's a better way to do this, stay tuned. this.x = x; this.y = y; } } ~~~ * this关键字指向了当前类的实例, 上面的代码可以简化为: ~~~ class Point { num x; num y; // Syntactic sugar for setting x and y // before the constructor body runs. Point(this.x, this.y); } ~~~ 4. ***构造函数不能继承(Constructors aren’t inherited)*** * Dart 语言中,子类不会继承父类的命名构造函数。如果不显式提供子类的构造函数,系统就提供默认的构造函数。 5. ***命名的构造函数(Named constructors)*** * 使用命名构造函数从另一类或现有的数据中快速实现构造函数。 ~~~ class Point { num x; num y; Point(this.x, this.y); // 命名构造函数Named constructor Point.fromJson(Map json) { x = json['x']; y = json['y']; } } ~~~ * 构造函数不能被继承,父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。 6. ***调用父类的非默认构造函数*** * 默认情况下,子类只能调用父类的无名,无参数的构造函数; 父类的无名构造函数会在子类的构造函数前调用; 如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序: 1. initializer list(初始化列表) 2. super class’s no-arg constructor(父类无参数构造函数) 3. main class’s no-arg constructor (主类无参数构造函数) * 如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须手打调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用`:(colon)`分割。 ~~~ class Person { String firstName; Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // 父类没有无参数的非命名构造函数,必须手动调用一个构造函数 super.fromJson(data) 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'; } ~~~ 7. ***初始化列表*** * 除了调用父类的构造函数,也可以通过初始化列表在子类的构造函数体前(大括号前)来初始化实例的变量值,使用逗号,分隔。如下所示: ~~~ class Point { num x; num y; Point(this.x, this.y); // 初始化列表在构造函数运行前设置实例变量。 Point.fromJson(Map jsonMap) : x = jsonMap['x'], y = jsonMap['y'] { print('In Point.fromJson(): ($x, $y)'); } } ~~~ ***注意:上述代码,初始化程序无法访问 this 关键字。*** 8. ***静态构造函数*** * 如果你的类产生的对象永远不会改变,你可以让这些对象成为编译时常量。为此,需要定义一个 const 构造函数并确保所有的实例变量都是 final 的。 ~~~ class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); } ~~~ 9. ***重定向构造函数*** * 有时候构造函数的目的只是重定向到该类的另一个构造函数。重定向构造函数没有函数体,使用冒号:分隔。 ~~~ class Point { num x; num y; // 主构造函数 Point(this.x, this.y) { print("Point($x, $y)"); } // 重定向构造函数,指向主构造函数,函数体为空 Point.alongXAxis(num x) : this(x, 0); } void main() { var p1 = new Point(1, 2); var p2 = new Point.alongXAxis(4); } ~~~ 10. ***常量构造函数*** * 如果类的对象不会发生变化,可以构造一个编译时的常量构造函数。定义格式如下: * 定义所有的实例变量是final。 * 使用const声明构造函数。 ~~~ class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const ImmutablePoint(0, 0); } ~~~ 11. ***工厂构造函数*** * 当实现一个使用 factory 关键词修饰的构造函数时,这个构造函数不必创建类的新实例。例如,工厂构造函数可能从缓存返回实例,或者它可能返回子类型的实例。 下面的示例演示一个工厂构造函数从缓存返回的对象: ~~~ class Logger { final String name; bool mute = false; // _cache 是一个私有库,幸好名字前有个 _ 。 static final Map<String, Logger> _cache = <String, Logger>{}; factory Logger(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final logger = new Logger._internal(name); _cache[name] = logger; return logger; } } Logger._internal(this.name); void log(String msg) { if (!mute) { print(msg); } } } ~~~ ***注意:工厂构造函数不能用 this。*** ### 抽象类 * 使用 abstract 修饰符来定义一个抽象类,该类不能被实例化。抽象类在定义接口的时候非常有用,实际上抽象中也包含一些实现。如果你想让你的抽象类被实例化,请定义一个 工厂构造函数 。 * 抽象类通常包含 抽象方法。下面是声明一个含有抽象方法的抽象类的例子: ~~~ // 这个类是抽象类,因此不能被实例化。 abstract class AbstractContainer { // ...定义构造函数,域,方法... void updateChildren(); // 抽象方法。 } ~~~ * 下面的类不是抽象类,因此它可以被实例化,即使定义了一个抽象方法: ~~~ class SpecializedContainer extends AbstractContainer { // ...定义更多构造函数,域,方法... void updateChildren() { // ...实现 updateChildren()... } // 抽象方法造成一个警告,但是不会阻止实例化。 void doSomething(); } ~~~ ### 类-隐式接口 * 每个类隐式的定义了一个接口,含有类的所有实例和它实现的所有接口。如果你想创建一个支持类 B 的 API 的类 A,但又不想继承类 B ,那么,类 A 应该实现类 B 的接口。 * 一个类实现一个或更多接口通过用 implements 子句声明,然后提供 API 接口要求。例如: ~~~ // 一个 person ,包含 greet() 的隐式接口。 class Person { // 在这个接口中,只有库中可见。 final _name; // 不在接口中,因为这是个构造函数。 Person(this._name); // 在这个接口中。 String greet(who) => 'Hello, $who. I am $_name.'; } // Person 接口的一个实现。 class Imposter implements Person { // 我们不得不定义它,但不用它。 final _name = ""; String greet(who) => 'Hi $who. Do you know who I am?'; } greetBob(Person person) => person.greet('bob'); main() { print(greetBob(new Person('kathy'))); print(greetBob(new Imposter())); } ~~~ * 这里是具体说明一个类实现多个接口的例子: ~~~ class Point implements Comparable, Location { // ... } ~~~ ### 类-扩展一个类 * 使用 extends 创建一个子类,同时 supper 将指向父类: ~~~ class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ... } class SmartTelevision extends Television { void turnOn() { super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ... } ~~~ * 子类可以重载实例方法, getters 方法, setters 方法。下面是个关于重写 Object 类的方法 noSuchMethod() 的例子,当代码企图用不存在的方法或实例变量时,这个方法会被调用。 ~~~ class A { // 如果你不重写 noSuchMethod 方法, 就用一个不存在的成员,会导致NoSuchMethodError 错误。 void noSuchMethod(Invocation mirror) { print('You tried to use a non-existent member:' + '${mirror.memberName}'); } } ~~~ * 你可以使用 @override 注释来表明你重写了一个成员。 ~~~ class A { @override void noSuchMethod(Invocation mirror) { // ... } } ~~~ * 如果你用 noSuchMethod() 实现每一个可能的 getter 方法,setter 方法和类的方法,那么你可以使用 @proxy 标注来避免警告。 ~~~ @proxy class A { void noSuchMethod(Invocation mirror) { // ... } } ~~~