2020-04-11 Flutter学习笔记——Dart构造函数
Dart构造函数有4种格式:
ClassName(...) //类名构造函数
Classname.identifier(...) //命名构造函数
const ClassName(...) //常量构造函数
factroy ClassName(...) //工厂构造函数
1. 默认构造函数
如果你定义了一个类,而没有定义构造函数,那么它将有一个默认的构造函数,这个构造函数没有参数
如果这个类有父类,那么默认构造函数,还会调用父类的无参数构造函数。
2. 类名构造函数
构造函数就是一个与类同名的函数,构造函数是一个函数,因此可以参数化。但是与函数不同,构造函数不能具有返回类型。
class Point {
num x, y;
// 类名构造函数
Point(num x, num y) {
// There's abetter way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
上例中只有两个成员变量,如果有10个,岂不是麻烦死?所以Dart有语法糖如下:
class Point {
num x, y;
// Syntacticsugar for setting x and y
// before theconstructor body runs.
Point(this.x,this.y);
}
它可以将x,y的赋值变得简单一些,就不用写构造函数的方法体了,记得括号后用分号哦。
3. 命名构造函数
在Dart中构造函数的名称可以是类名 ClassName 或者类名和标识符 ClassName.identifier 。 其中构造函数名称是“ClassName”的函数叫“类名构造函数”;构造函数名称是“ClassName.identifier”的函数叫“命名构造函数”。
class Point {
num x, y;
// 类名构造函数
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x= 0;
y= 0;
}
// 命名构造函数
Point.now(num x, num y)
{
print("这是命名构造函数");
this.x = x;
this.y = y;
}
}
使用命名构造函数可以为类提供多个构造函数,以使类定义多个构造函数。
请记住,命名构造函数不可继承,如果子类想要有 和父类一样的命名构造函数,那就写个同名的(通常也会在子类的命名构造函数里,调用父类的同名命名构造函数)
再看一个实际例子,加深一下用命名构造函数的好处:横向扩展
RaisedButton 即"漂浮"按钮,它默认带有阴影和灰色背景。按下后,阴影会变大,如图3-10所示:
使用RaisedButton非常简单,如:
RaisedButton( //类名构造函数
child: Text("normal"),
onPressed: () {},
);
RaisedButton、FlatButton、OutlineButton都有一个icon 构造函数,通过它可以轻松创建带图标的按钮,如图3-14所示:
代码如下:
RaisedButton.icon( //命名构造函数
icon:Icon(Icons.send),
label:Text("发送"),
onPressed: _onPressed,
),
OutlineButton.icon(
icon:Icon(Icons.add),
label:Text("添加"),
onPressed:_onPressed,
),
FlatButton.icon(
icon:Icon(Icons.info),
label:Text("详情"),
onPressed:_onPressed,
),
3.2重定向构造函数(构造函数传递)
有时构造函数的唯一目的是重定向到同一类中的另一个构造函数。定义构造函数的时候,除了一个普通构造函数,还可以有若干命名构造函数,这些构造函数之间,有时候会有一些相同的逻辑,如果分别书写在各个构造函数中,会有些多余,所以构造函数可以传递。重定向构造函数没有方法体,会在初始化列表中,构造函数调用出现在冒号( :)之后 。 就是在创建类时,定义一个命名构造函数,但是这个构造函式的主体不实现,直接通过另外一个构造函数实现对外界传入的参数接收并赋值给内部的变量。
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(Map<String, num> json)
:x = json['x'],
y= json['y'] {
print('In Point.fromJson(): ($x, $y)');
} //初始化列表就是构造函数名的冒号后面,打括号前面的部分。
}
初始化列表的执行顺序,在整个构造函数的最前面,它除了可以调用父类的构造函数,还可以在构造函数方法体之前,初始化一些成员变量。
import 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
:x = x,
y= y,
distanceFromOrigin= sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
再看计数器示例的代码,是不是有感觉了:语法糖和初始化列表都出现了
class MyHomePage extends StatefulWidget {
MyHomePage({Key key
, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
4. 常量构造函数
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
如果你的类,创建的对象永远不会改变,你可以在编译期就创建这个常量实例,并且定义一个常量构造函数,并且确保所有的成员变量都是final的。
5. 工厂构造函数
有时候可能有一种需求,并不需要每次都创建新的类实例,而是每一种情况,只需要一个实例:
class Logger {
final String name;
bool mute= false;
// _cache is library-private, thanks to the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
main() {
var logger = Logger('UI');
logger.log('Button clicked');
}
工厂构造函数,没有权利访问this
上例的意思是,类中又一个静态缓存_cache保存着一些Logger类实例,创建实例时,给工厂构造函数传递的name,如果在缓存中已经存在,就用缓存中现成的实例,如果没有,就新建一个实例,并且也放到缓存中。
如此这般,我们可以创建名字为UI / SYS / API 等的实例,然后在debug的时候,如果设置名字为UI的Logger实例的mute为true,就不会打印UI相关的log,而不影响其它两个名字的log。是不是很方便呢?
尾记:
学习Dart语法时建议结合实际例子中的用法去理解,语法特性也是为实际场景服务的,掌握背后的意义,才能真正掌握一门工具,用好一门语言。