玩转dart类
前言
Dart 是支持基于 mixin 继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null 以外的所有的类都继承自 Object
类。 基于 mixin 的继承 意味着尽管每个类(top class Object?
除外)都只有一个超类,一个类的代码可以在其它多个类继承中重复使用。扩展方法
是一种在不更改类或创建子类的情况下向类添加功能的方式。
1、类的成员
对象的 成员 由函数和数据(即 方法 和 实例变量)组成。方法的 调用
要通过对象来完成,这种方式可以访问对象的函数和数据。
- 下面是声明实例变量的示例:
class Point {
int? x; // int? 可空类型
int? y;
int z = 0;
}
如上所示 int?
所有未初始化的实例变量其值均为null
- 使用(.)来访问对象的实例变量或方法:
var point = Point(2, 3);
print(point.x);
print(point.y);
- 使用 ?. 代替. 可以避免因为左边表达式为null 而导致的问题 (这个是flutter 2.0 之后新增的空认证功能)
print(point?.x);
print(point?.y);
- 如下代码所示
p1.y = 6;
setter 写入方法,print(p1.y);
getter 读取方法
p1.y = 6;
print(p1.y);
2、构造函数
可以使用 构造函数 来创建一个对象。构造函数的命名方式可以为
类名
或类名 . 标识符
的形式。例如下述代码分别使用
Point()
和Point.fromJson()
两种构造器创建了Point
对象:
class Point {
int? x;
int? y;
Point(this.x, this.y);
Point.fromJson(Map map) {
x = map['x'];
y = map['y'];
}
}
- 下面两段代码,执行的结果都是获取的对应的x 的值
var p1 = Point(2, 3);
print(p1.x);
var p2 = Point.fromJson({'x': 2, 'y': 3});
print(p2.x);
3、常量构造函数
实例变量可以是
final
,在这种情况下,它们必须只设置一次。在声明时,使用构造函数形参或构造函数的初始化列表初始化最终的非延迟实例变量:
class ProfileMark{
final String name;
final DateTime start = DateTime.now();
ProfileMark(this.name);
ProfileMark.unNamed() : name = '';
}
- 一些类提供了常量构造函数。使用常量构造函数,在构造函数名之前加
const
关键字,来创建编译时常量时:
class ImmutablePoint {
final int? x;
final int? y;
const ImmutablePoint(this.x, this.y);
}
- 下面代码中的 p1 和 p2 的执行结果是true, 它们其实是同一个实例
var p1 = const ImmutablePoint(1, 1);
var p2 = const ImmutablePoint(1, 1);
assert(p1 == p2);
- 在 常量上下文 场景中,你可以省略掉构造函数或字面量前的
const
关键字。例如下面的例子中我们创建了一个常量 Map:
const pointAndLine = const{
'point': const[const ImmutablePoint(1, 1)],
'line': const[const ImmutablePoint(1, 10), const ImmutablePoint(1, 11)],
};
- 根据上下文,可以只保留一个
const
关键字,其余的全部省略:
const pointAndLine = {
'point': [ImmutablePoint(1, 1)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(1, 11)],
};
- 但是如果无法根据上下文判断是否可以省略
const
,则不能省略掉const
关键字,否则将会创建一个 非常量对象 例如:
var p1 = const ImmutablePoint(1, 1);
var p2 = ImmutablePoint(1, 1);
assert(p1 == p2);
- 上面代码执行结果会抛出异常:
4、默认构造函数
如果你没有声明构造函数,那么 Dart 会自动生成一个无参数的 构造函数并且该构造函数会调用其父类的 无参数构造方法。
5、构造函数不被继承
子类不会继承父类的构造函数,如果子类没有声明构造函数,那么只会有一个默认无参数的构造函数。
6、命名式构造函数
可以为一个类声明多个命名式构造函数来表达更明确的意图:
class Point {
final int? x; // int? 可空类型
final int? y;
Point(this.x, this.y);
// 命名构造函数,上面讲过
Point.origin()
: x = xOrigin,
y = yOrigin;
}
记住构造函数是不能被继承的,这将意味着子类不能继承父类的命名式构造函数,如果你想在子类中提供一个与父类命名构造函数名字一样的命名构造函数,则需要在子类中显式地声明。
7、调用父类非默认构造函数
默认情况下,子类的构造函数会调用父类的匿名无参数构造方法,并且该调用会在子类构造函数的函数体代码执行前,如果子类构造函数还有一个
初始化列表,那么该初始化列表会在调用父类的该构造函数之前被执行,总的来说,这三者的调用顺序如下:
- 初始化列表
- 父类的无参数构造函数
- 当前类的构造函数
如果父类没有匿名无参数构造函数,那么子类必须调用父类的其中一个构造函数,为子类的构造函数指定一个父类的构造函数只需在构造函数体前使用(:)指定。
下面的示例中,Employee 类的构造函数调用了父类 Person 的命名构造函数。
class Person{
String? firstName;
Person.formJson(Map map){
print("进入了Person 的命名函数中、、、");
}
}
class Employee extends Person{
// Person没有默认构造函数;
//你必须重新Employee.formJson 并调用 super.fromJson(data)。
Employee.formJson(Map map) : super.formJson(map){
print("进入了 Employee 子类");
}
}
main{
var employee = Employee.formJson({});
print(employee);
}
上面实例代码打印如下:
因为参数会在子类构造函数被执行前传递给父类的构造函数,因此该参数也可以是一个表达式,比如一个函数:
class Employee extends Person {
Employee() : super.fromJson(fetchDefaultData());
// ···
}
8、获取对象的类型
可以使用
Object
对象的runtimeType
属性在运行时获取一个对象的类型,该对象类型是 Type 的实例。
print('p1 的实例类型是 ${p1.runtimeType}');
9、初始化列表
除了调用父类构造函数之外,还可以在构造函数体,执行之前初始化实例变量。每个实例变量之间使用逗号分隔。
class Point {
final int? x; // int? 可空类型
final int? y;
Point.fromJson(Map map)
: x = map['x'],
y = map['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
- 代码调用
Point.fromJson({'x': 2, 'y': 3});
- 运行结果如下:
- 在开发模式下,你可以在初始化列表中使用 assert 来验证输入数据:
Point.withAssert(this.x, this.y) : assert(x! > y!){
print('In Point.withAssert(): ($x, $y)');
}
上面代码中,如果 assert(x! > y!)
不为true,会直接抛出异常
- 使用初始化列表设置
final
字段非常方便,下面的示例中就使用初始化列表来设置了三个final
变量的值。
class Point {
final double? x; // int? 可空类型
final double? y;
final double distanceFromOrigin;
Point(double x, double y)
: x = x,
y = y,
distanceFromOrigin = (x * x + y * y);
}
main{
var p = Point(2, 5);
print(p.distanceFromOrigin);
}
运行结果为:
10、重定向构造函数
有时候类中的构造函数仅用于调用类中其它的构造函数,此时该构造函数没有函数体,只需在函数签名后使用(:)指定需要重定向到的其它构造函数 (使用
this
而非类名):
class Point{
double x, y;
// this 主构造函数
Point(this.x, this.y);
Point.alongXAxis(double x): this(x, 0);
}
main{
var p = Point(2, 5);
print(p.y);
print(p.x);
var p1 = Point.alongXAxis(10);
print(p1.y);
print(p1.x);
}
运行结果如下:
11、工厂构造函数
使用
factory
关键字标识类的构造函数将会令该构造函数变为工厂构造函数,这将意味着使用该构造函数构造类的实例时并非总是会返回新的实例对象。例如,工厂构造函数可能会从缓存中返回一个实例,或者返回一个子类型的实例。
- 在如下的示例中,
Logger
的工厂构造函数从缓存中返回对象,和Logger.fromJson
工厂构造函数从 JSON 对象中初始化一个最终变量。
class Logger{
final String name;
// 带有 _cache 表示是私有的
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name){
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
factory Logger.fromJson(Map<String, Object> json){
return Logger(json['name'].toString());
}
Logger._internal(this.name);
}
- 工厂构造函数的调用方式与其他构造函数一样:
main{
var logger = Logger("tiger");
print(logger.name);
var logger2 = Logger.fromJson({'name': 'TIGER'});
print(logger2.name);
}
总结
🎉🎉🎉 很高兴你能看到这里,这篇文章包含了目前flutter 中关于类的部分情况,读到这里,你该犒劳一下自己了哦,快去买杯奶茶吧!!!