1.获取运行时对象类型
使用Object 属性的 runtimeType,它返回一个 Type 对象。
print('a 的类型是 ${a.runtimeType}');
⚠️警告
在测试对象的类型时建议使用object is Type
比测试 object.runtimeType == Type 更稳定。
2.实例变量的声明
class Point {double? x; // 声明实例变量 x,初始值为 null。double? y; // 声明 y,初始值为 null。double z = 0; // 声明 z,初始值为 0。
}
所有实例变量都会生成一个隐式的 getter 方法。非final的实例变量,以及未初始化的late final实例变量,都会生成一个隐式的setter方法。
class Point {double? x; // 声明实例变量 x,初始值为 null。double? y; // 声明 y,初始值为 null。
}void main() {var point = Point();point.x = 4; // 使用 x 的 setter 方法。assert(point.x == 4); // 使用 x 的 getter 方法。assert(point.y == null); // 默认值为 null。
}
在声明时初始化非 late 实例变量会在实例创建时设置值,在构造函数及其初始化列表执行之前。因此,非 late 实例变量的初始化表达式(= 之后的表达式)无法访问 this。
double initialX = 1.5;class Point {// 可以访问不依赖 `this` 的声明:double? x = initialX;// 错误,在非 `late` 初始化器中无法访问 `this`:double? y = this.x;// 可以,在 `late` 初始化器中访问 `this`:late double? z = this.x;// 可以,`this.x` 和 `this.y` 是参数声明,不是表达式:Point(this.x, this.y);
}
实例变量可以是 final 的,在这种情况下它们必须被设置且仅设置一次。可以在声明时
、使用构造函数参数
或者使用构造函数的初始化列表
来初始化final类型的非late实例变量。
class ProfileMark {final String name;final DateTime start = DateTime.now();ProfileMark(this.name); //使用构造函数参数,来初始化final类型的非late实例变量ProfileMark.unnamed() : name = ''; //使用构造函数的初始化列表,来初始化final类型的非late实例变量
}
如果您需要在构造函数体开始后为 final 实例变量赋值,可以使用以下方法之一:
- 使用工厂构造函数。
- 使用
late final
,但要小心:没有初始值设定器的late final
会为 API 添加一个 setter。
3.隐式接口
每个类都隐式地定义了一个接口,该接口包含该类的所有实例成员以及它所实现的任何接口的实例成员。如果你想创建一个支持类 B 的应用程序编程接口(API)但又不继承 B 的实现的类 A,那么类 A 应该实现 B 接口。
implements
关键字主要用于类实现接口
-
概念:接口规定了一组方法的签名,不过不包含具体实现。类借助 implements 关键字来实现接口时,就必须实现接口里定义的所有方法
-
使用方法:在类定义时,运用 implements 关键字,后面紧跟一个或多个接口名,若有多个接口,用逗号分隔。类需要实现接口中定义的所有方法。
-
示例
// 定义一个接口
abstract class Animal {void eat();void sleep();
}// 实现接口的类
class Dog implements Animal {void eat() {print('Dog is eating.');}void sleep() {print('Dog is sleeping.');}
}void main() {Dog dog = Dog();dog.eat();dog.sleep();
}
在上述代码中,Animal 是一个接口,定义了 eat 和 sleep 方法。Dog 类使用 implements 关键字实现了 Animal 接口,并且实现了接口中定义的所有方法。
与 extends 的区别
- extends:用于类的继承,一个类只能单继承,子类会继承父类的实现。
- implements:用于实现接口,一个类可以实现多个接口,类需要自己实现接口中定义的所有方法
一个类通过在 implements 子句中声明一个或多个接口,然后提供这些接口所要求的 API 来实现它们。
以上来自AI的解释,下面是dart官方文档的
类通过在 implements 子句中声明一个或多个接口并提供接口所需的 API 来实现它们。例如:
// 一个 person类. 包含一个隐式接口greet().
class Person {// 在接口中,但仅在此类中可见.final String _name;// 不在接口中,因为这是一个构造函数.Person(this._name);// 在接口中.String greet(String who) => 'Hello, $who. I am $_name.';
}// Person 接口的一个实现类.
class Impostor implements Person {String 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'))); //你好,Bob。我是 Kathyprint(greetBob(Impostor())); //嗨 Bob。你知道我是谁吗?
}
4.类变量和方法
使用 static 关键字来实现类级的变量和方法。
- 静态变量
静态变量(类变量)对于类范围的状态和常量非常有用
class Queue {static const initialCapacity = 16;// ···
}void main() {assert(Queue.initialCapacity == 16); //静态成员/方法无需创建实例,直接访问/调用
}
静态变量只有在使用时才会被初始化
- 静态方法
静态方法(类方法)不作用于实例,因此无法访问 this。但是,它们可以访问静态变量。如下例所示,您直接在类上调用静态方法:
import 'dart:math';class Point {double x, y;Point(this.x, this.y);static double distanceBetween(Point a, Point b) {var dx = a.x - b.x;var dy = a.y - b.y;return sqrt(dx * dx + dy * dy);}
}void main() {var a = Point(2, 2);var b = Point(4, 4);var distance = Point.distanceBetween(a, b);assert(2.8 < distance && distance < 2.9);print(distance);
}
⚠️注意
对于常用或广泛使用的工具和功能,考虑使用顶级函数,而不是静态方法。
您可以将静态方法用作编译时常量。例如,您可以将静态方法作为参数传递给常量构造函数。