Dart基础知识-构造方法
常规的的构造函数
class Point {
double x = 0;
double y = 0;
Point(double x, double y) {
this.x = x;
this.y = y;
}
}
这种方式在Dart中并不推荐使用,更多的使用的是形式参赛构造函数
形式参赛构造函数
class Point {
final double x;
final double y;
//x和y在构造之前赋值
Point(this.x, this.y);
}
缺省的构造函数
如果不提供构造函数,这里将会有一个默认的构造函数,默认构造没有参数,同时默认调用父类的默认构造函数.
命名构造函数
我们还可以使用命名构造函数使构造函数构造的对象的目的更加清晰.
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
Point(this.x, this.y);
// 命名构造函数
Point.origin()
: x = xOrigin,
y = yOrigin;
}
但是命名构造函数并不参与继承关系,子类是不会继承父类的命名构造函数。
有继承关系的构造函数
如果子类的构造函数是未命名,无参数的构造函数(就是没有自己写构造函数),那么构造子类时候这个默认构造函数
会调用父类的未命名,无参数的构造函数。如果子类提供命名的构造函数或者有参数的构造函数,那么需要制定父类的
构造函数。指定超类的构造函数使用( :)后跟上对应父类的构造函数在子类构造体之前。说起来比较拗口,看个示
范久明白了.
class Person {
String? firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson().
Employee.fromJson(super.data) : super.fromJson() {
print('in Employee');
}
}
void main() {
var employee = Employee.fromJson({});
print(employee);
}
输出内容: in Person in Employee Instance of 'Employee'
因为参数的赋值在构造对象之前,所以参数可以为一个表达式
class Employee extends Person {
Employee() : super.fromJson(fetchDefaultData());
// ···
}
由于参数的赋值在构造对象之前,所以如果是表达式是不能使用this关键字的.
超类参数
子类为了避免在构造函数中手动传入初始化参数给父类,可以利用父类初始化参数传递的方式去简化实现,父类初始化
参数的语法与形式化参数类似.
class Vector2d {
final double x;
final double y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// 这实是父类初始化参数传递方式简化了繁琐实现如下.
// Vector3d(final double x, final double y, this.z) : super(x, y);
Vector3d(super.x, super.y, this.z);
}
如果是命名参数构造函数可以参考如下
class Vector2d {
// ...
Vector2d.named({required this.x, required this.y});
}
class Vector3d extends Vector2d {
// ...
// 传递参数y到超类的命名构造函数
// Vector3d.yzPlane({required double y, required this.z})
// : super.named(x: 0, y: y);
Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}
List初始化方式
在构造函数后还能够跟一些表达式用于赋值操作
例如我们可以这样构造Point对象
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}
在(:)可以跟多个表达式,所以说这是一种List初始化方式,但是这里必须注意表达式不能访问this.
我们还能利用List初始化方式做一些有趣的操作如下。
class Point {
final double x;
final double y;
final double distanceFromOrigin;
Point(double x, double y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
void main() {
var p = Point(2, 3);
print(p.distanceFromOrigin);
}
distanceFromOrigin的赋值实际上是通过一个函数计算后赋值的.
重定向构造器
构造函数的调用可以重定向到另外一个构造函数的调用。
class Point {
double x, y;
Point(this.x, this.y);
// 重定向构造调用 Point(this.x, this.y)
Point.alongXAxis(double x) : this(x, 0);
}
常量构造器
如果你想构造的对象永远不会改变,可以把构造方法定义为常量(加上const)。
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
工厂构造器
这是所有构造方式中我觉得最有趣的方式,使用factory关键字去声明构造方法,可以不必每次都创建实例,使用
factory声明构造方法可以从缓存中返回实例,另外还有一个用处是逻辑化初始化final成员,这种操作可不能通
过List初始化方式完成。
举个例子: 下面的代码中,Logger factory构造从cache中返回对象,Logger.fromJson的factory构造初始化final成
员,这里初始化值来自于JSON对象属于逻辑化初始方式。
class Logger {
final String name;
bool mute = false;
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);
void log(String msg) {
if (!mute) print(msg);
}
}
调用方式上实际上和普通的构造方法没有区别,但是实际上走的factory构造方式的逻辑.
var logger = Logger('UI');
logger.log('Button clicked');
var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);