1、不指定构造函数,dart会为你创建一个默认的无参的构造函数
2、如果指定构造函数
和Java类似,dart中可以使用和class名称相同的函数作为其构造函数,例
class Student {
int age = 0;
int id = 0;
Student(int age, int id) {
this.age = age;
this.id = id;
}
}
上面的this表示的是当前类的实例,对dart来说,this是可以忽略的,但是在上面的例子中,因为类变量的名字和构造函数传入的参数的名字是一样的,所以需要加上this来进行区分。
简写方式:
class Student {
int age = 0;
int id = 0;
Student(this.age, this.id);
}
区别于其他语言,dart还可以使用命名构造函数
格式为
ClassName.identifier
例
class Student {
int age = 0;
int id = 0;
Student(this.age, this.id);
Student.fromJson(Map data) {
print("命名构造函数Student.fromJson");
}
}
命名构造函数不可继承,如果子类想要有和父类一样的命名构造函数,那就写个同名的(通常也会在子类的命名构造函数里,调用父类同名的命名构造函数)
dart中的类是可以继承的,那么下面我们来探究一下dart的子类的构造函数的执行顺序
如果不给dart类指定构造函数,那么dart会为类自动生成一个无参的构造函数,如果这个类是子类的话,则会自动调用父类的无参构造函数。
对于子类的构造函数来说,初始化的时候有三步:
1、调用初始化列表
2、调用父类的构造函数
3、调用自己的构造函数
在第2步中,如果父类没有默认的无参构造函数,则需要手动指定具体的父类构造函数。怎么调用呢?
可以直接在子类的构造函数后面使用:操作符接上父类的构造函数,例
class Student {
String? firstName;
Student.fromJson(Map data) {
print("in Student");
}
}
class Jone extends Student {
Jone.fromJson(Map data) : super.from(data) {
print("in Jone");
}
}
理解了父类的构造函数之后,我们再来看一下初始化列表
初始化列表就是在构造函数执行之前执行的代码,和调用父类的构造函数一样,也使用:操作符,例
Point.fromJson(Map json) : x = json['x']! , y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
初始化列表很有用,尤其是初始化那些final修饰的成员变量时,因为在方法体中,不能给final修饰的成员变量赋值
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);
}
late
进行修饰///不推荐
class Test{
late int x;
Test(int p){
x = p + 1;
}
}
///推荐
class Test{
int x;
Test(int p)
: x = p + 1;
}
如果一个构造函数需要调用另一个构造函数,而其本身并不进行任何变动,这可以使用重定向构造函数,重定向构造函数也是用:操作符,后面跟的另外的构造函数,例
class Point {
double x,y;
// 主构造函数
Point(this.x, this.y);
// 重定向构造函数
Point.alongXAxix(double x) : this(x,0);
}
如果对象中的属性在创建之后,是不会变化的,则可以使用Constant构造函数,也就是在构造函数前面加上const修饰符,初始化的所有属性都要以final来修饰:
class ImmutablePoint {
static const ImmutalbePoint origin = ImmutablePoint(0,0);
final double x, y;
const Immutable(this.x, this.y)
}
默认情况下,dart类中的构造函数返回的是该类的新实例,但是我们在实际的应用开发中可能会对返回的对象做些选择,比如从缓存中返回已经存在的对象,或者返回该类具体的实现子类。
为了实现这样的功能,dart中专门有一个Factory关键字,使用Factory的构造函数叫做工厂构造函数。
class Student {
final String name;
static final Map _studentMap = {};
factory Student(String name) {
return _studentMap.putIfAbsent(name, ()=>Student._newStudent(name));
}
factory Student.fromJson(Map json) {
return Student(json['name'].toString())
}
Student._newStudent(this.name);
}
注意1,dart中只能有一个未命名的构造函数,对应命名构造函数来说,名字不能重复,否则会报The default constructor is already defined异常
注意2,工厂构造函数,没有权利访问this
上面的代码中,factory Student是一个未命名构造函数,而factory Student.fromJson则是一个命名构造函数。
所以如果你再给Student类添加一个未命名构造函数,如下
Student(this.name);
则会报错。
那么问题来了,factory构造函数和普通构造函数到底有什么区别呢?
他们最大的区别就是普通构造函数是没有返回值的,而factory构造函数需要一个返回值。
factory用来实现单例,详见 文章下面的单例模式
class Singleton {
static final Singleton _singleton = Singleton.internal();
factory Singleton() => _singleton;
Singleton.internal();
}
工厂构造函数
可以与命名构造函数
、常量构造函数
结合使用
///常量构造函数
const TestModule(this.x);
///工厂构造函数 + 命名构造函数
factory TestModule.fromJson(Map map){
...省略
return TestModule(map['x']);
}
使用_
下划线进行私有声明
class TestModule {
//私有构造函数
TestModule._origin();
}
总结,Dart构造函数有4中格式:
ClassName(...) // 普通构造函数
ClassName.identifier(...) // 命名构造函数
const ClassName(...) // 常量构造函数
factory ClassName(...) // 工厂构造函数
参考:
https://www.jianshu.com/p/24d0927f7625
https://juejin.cn/post/6844903902563794958
https://segmentfault.com/a/1190000040956384
https://dart.cn/guides/language/language-tour#constructors
一般来说,要在代码中使用单例模式,结构上会有下面这些约定俗成的要求:
getInstance()
访问。遵循以上这些要求,我们就不难能用 Dart 写出一个普通的单例模式:
class Singleton {
static Singleton _instance;
// 私有的命名构造函数
Singleton._internal();
static Singleton getInstance() {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
同时,在实现单例模式时,也需要考虑如下几点,以防在使用过程中出现问题:
在实际编码过程中,单例模式常见应用有:
如上文所说的,Dart 语言作为单线程模型的语言,实现单例模式时,我们本身已经可以不用再去考虑 线程安全 的问题了。Dart 的很多其他特性也依然可以帮助到我们实现更加 Dart 化的单例。
使用 getter 操作符,可以打破单例模式中既定的,一定要写一个 getInstance()
静态方法的规则,简化我们必须要写的模版化代码,如下的 get instance
:
class Singleton {
static Singleton _instance;
static get instance {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
Singleton._internal();
}
Dart 的 getter 的使用方式与普通方法大致相同,只是调用者不再需要使用括号,这样,我们在使用时就可以直接使用如下方式拿到这个单例对象:
final singleton = Singleton.instance;
而 Dart 中特有的 工厂构造函数(factory constructor)也原生具备了 不必每次都去创建新的类实例 的特性,将这个特性利用起来,我们就可以写出更优雅的 Dart(able) 单例模式了,如下:
class Singleton {
static Singleton _instance;
Singleton._internal();
// 工厂构造函数
factory Singleton() {
if (_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
这里我们不再使用 getter 操作符额外提供一个函数,而是将单例对象的生成交给工厂构造函数,此时,工厂构造函数仅在第一次需要时创建 _instance
,并之后每次返回相同的实例。这时,我们就可以像下面这样使用普通构造函数的方式获取到单例了:
final singleton = Singleton();
如果你还掌握了 Dart 空安全及箭头函数等特性,那么还可以使用另一种方式进一步精简代码,写出像下面这样 Dart 风味十足的代码:
class Singleton {
static Singleton _instance;
Singleton._internal() {
_instance = this;
}
factory Singleton() => _instance ?? Singleton._internal();
}
这里,使用 ??
作为 _instance
实例的判空操作符,如果为空则调用构造函数实例化否则直接返回,也可以达到单例的效果。
以上,Dart 单例中懒加载的无不是使用判空来实现的(if (_instance == null)
或 ??
),但是在 Dart 空安全特性里还有一个非常重要的操作符 late
,它在语言层面就实现了实例的懒加载,如下面这个例子:
class Singleton {
Singleton._internal();
factory Singleton() => _instance;
static late final Singleton _instance = Singleton._internal();
}
被标记为 late
的变量 _instance
的初始化操作将会延迟到字段首次被访问时执行,而不是在类加载时就初始化。这样,Dart 语言特有的单例模式的实现方式就这么产生了。
单例参考:
https://flutter.cn/community/tutorials/singleton-pattern-in-flutter-n-dart