news 2026/4/18 8:01:21

Android学Dart学习笔记第十六节 类-构造方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android学Dart学习笔记第十六节 类-构造方法

序言

在dart中,允许多种形式的构造方法,上篇类中,也有涉略。在这篇文章中我们进行深入的学习。

构造 方法的类型

Generative constructors、Default constructors、Named constructors、Constant constructors、Redirecting constructors、Factory constructors、Redirecting factory constructors以及Constructor tear-offs
这些都是官方文档中给到的构造方法的类型,下面我们逐一了解

Generative constructors

是一种类似java的构造函数,基础的

classPoint{// Instance variables to hold the coordinates of the point.double x;double y;// Generative constructor with initializing formal parameters:Point(this.x,this.y);}

Default constructors

默认构造,当你没有明确声明构造方法时,就会使用这个

Named constructors

命名构造,当你给构造函数起了一个其他名字时,就是命名构造,但命名必须遵循class.name的形式,如下

constdouble xOrigin=0;constdouble yOrigin=0;classPoint{finaldouble x;finaldouble y;// Sets the x and y instance variables// before the constructor body runs.Point(this.x,this.y);// Named constructorPoint.origin():x=xOrigin,y=yOrigin;}

子类不会继承父类的命名构造

Constant constructors

如果类生成不变的对象,将这些对象设置为编译时常量。要使对象成为编译时常量,定义一个const构造函数,并将所有实例变量设置为final

classImmutablePoint{staticconstImmutablePoint origin=ImmutablePoint(0,0);finaldouble x,y;constImmutablePoint(this.x,this.y);}

常量构造函数并不总是创建常量。它们可以在非常量上下文中调用。
如下

voidmain(){constImmutablePoint p=ImmutablePoint(10,20);varp1=constImmutablePoint(10,20);varp2=ImmutablePoint(10,20);//这个叫非常量上下文print(p==p1);//trueprint(p==p2);//falseprint(p1==p2);//false}classImmutablePoint{staticconstImmutablePoint origin=ImmutablePoint(0,0);finaldouble x,y;constImmutablePoint(this.x,this.y);}

Redirecting constructors

构造函数可以重定向到同一个类中的另一个构造函数。重定向构造函数有一个空主体。构造函数使用this代替冒号(:)后面的类名。

classPoint{double x,y;// The main constructor for this class.Point(this.x,this.y);// Delegates to the main constructor.Point.alongXAxis(double x):this(x,0);}

Factory constructors

这个上篇文章也讲到了

当遇到下列两种实现构造函数的情况之一时,请使用factory关键字:
构造函数并不总是创建类的新实例。尽管工厂构造函数不能返回null,但它可能返回:

从缓存中创建一个现有实例,而不是创建一个新的实例 子类型的新实例

在构建实例之前,你需要执行一些重要的工作。这可能包括检查参数或进行任何在初始化列表中无法处理的其他处理。

varlogger=Logger('UI');logger.log('Button clicked');varlogMap={'name':'UI'};varloggerJson=Logger.fromJson(logMap);classLogger{finalString name;bool mute=false;// _cache is library-private, thanks to// the _ in front of its name.staticfinalMap<String,Logger>_cache=<String,Logger>{};factoryLogger(String name){return_cache.putIfAbsent(name,()=>Logger._internal(name));}factoryLogger.fromJson(Map<String,Object>json){returnLogger(json['name'].toString());}Logger._internal(this.name);voidlog(String msg){if(!mute)print(msg);}}

工厂构造不能访问this

Redirecting factory constructors

有了上面的经验这个也不难理解了

factoryListenable.merge(List<Listenable>listenables)=_MergingListenable

普通的工厂构造函数似乎可以创建和返回其他类的实例。这将使重定向工厂变得不必要。
重定向工厂有几个优点:

抽象类可能会提供一个使用另一个类的常量构造函数的常量构造函数。
重定向工厂构造函数避免了转发器重复形式参数及其默认值的需要。

Constructor tear-offs

支持撕开,看下面的例子

finalList<int>charCodes=[72,101,108,108,111,33];// "Hello!"// Use a tear-off for a named constructor:varstrings=charCodes.map(String.fromCharCode);// Use a tear-off for an unnamed constructor:varbuffers=charCodes.map(StringBuffer.new);



上面的代码中使用了lamada,去除后如下

// Instead of a lambda for a named constructor:varstrings=charCodes.map((code)=>String.fromCharCode(code));// Instead of a lambda for an unnamed constructor:varbuffers=charCodes.map((code)=>StringBuffer(code));

Instance variable initialization 实例变量初始化

dart中有3种变量初始化方式

Initialize instance variables in the declaration

声明时初始化

classPointA{double x=1.0;double y=2.0;// The implicit default constructor sets these variables to (1.0,2.0)// PointA();@overrideStringtoString(){return'PointA($x,$y)';}}

Use initializing formal parameters

使用初始化形式参数,初始化参数也可以是可选的

classPointB{finaldouble x;finaldouble y;// Sets the x and y instance variables// before the constructor body runs.PointB(this.x,this.y);// 初始化参数也可以是可选参数PointB.optional([this.x=0.0,this.y=0.0]);}

私有字段不能用作命名初始化形式变量。可以使用下面的方式

classPointB{// ...PointB.namedPrivate({required double x,required double y}):_x=x,_y=y;// ...}

上面的写法也适用于命名变量

classPointC{double x;// must be set in constructordouble y;// must be set in constructor// Generative constructor with initializing formal parameters// with default valuesPointC.named({this.x=1.0,this.y=1.0});@overrideStringtoString(){return'PointC.named($x,$y)';}}// Constructor using named variables.finalpointC=PointC.named(x:2.0,y:2.0);

所有通过初始化形式参数引入的变量都是final变量,并且只在初始化变量的作用域内。

要执行无法在初始化列表中表达的逻辑,请使用该逻辑创建工厂构造函数或静态方法。然后,您可以将计算值传递给普通构造函数。

构造函数的参数可以声明为可空类型,并且可以被初始化或不被初始化

classPointD{double?x;// null if not set in constructordouble?y;// null if not set in constructor// Generative constructor with initializing formal parametersPointD(this.x,this.y);@overrideStringtoString(){return'PointD($x,$y)';}}

Use an initializer list

使用初始化列表

在运行构造函数体之前,可以初始化实例变量。用逗号分隔初始化方法。

// Initializer list sets instance variables before// the constructor body runs.Point.fromJson(Map<String,double>json):x=json['x']!,y=json['y']!{print('In Point.fromJson(): ($x, $y)');}

初始化列表的右侧不能访问this

在开发期间,可以通过初始话列表使用assert验证输入参数

Point.withAssert(this.x,this.y):assert(x>=0){print('In Point.withAssert(): ($x, $y)');}

初始化列表也可以用来设置final值

如下

classA{finalint x;// A(this.x);A(int u):x=u;}

Constructor inheritance

构造函数的继承

子类不会从超类或直接父类继承构造方法。如果类没有声明构造方法,那么只能使用默认的构造方法。

类可以继承超类的参数。这些称为超参数(super parameter)

构造函数的工作方式与调用静态方法链的方式有些类似。每个子类都可以调用超类的构造函数来初始化实例,就像子类调用超类的静态方法一样。这个过程不会“继承”构造函数体或签名。

Non-default superclass constructors

非默认超类构造

Dart按以下顺序执行构造函数:
初始化器列表
超类的未命名、无参数的构造函数
主类的无参数构造函数

如果超类没有未命名的、无参数的构造方法,就调用超类中的一个构造方法。在构造函数主体之前(如果有的话),在冒号(:)之后指定超类的构造函数。

classPerson{String?firstName;Person.fromJson(Map data){print('in Person');}}classEmployeeextendsPerson{// Person does not have a default constructor;// you must call super.fromJson().Employee.fromJson(Map data):super.fromJson(data){print('in Employee');}}

由于Dart在调用构造函数之前计算超类构造函数的参数,因此参数可以是类似于函数调用的表达式

classEmployeeextendsPerson{Employee():super.fromJson(fetchDefaultData());// ···}

超类构造函数的参数无法访问this。例如,参数可以调用静态方法,但不能调用实例方法

Super parameters

为了避免将每个参数都传入构造函数的父类调用中,可以使用 super-initializer 参数(超类初始化参数)将参数直接转发到指定或默认的父类构造函数。

此功能不能与重定向构造函数一起使用。

classDogextendsAnimal{finalString breed;// 传统方式:需要将 name 手动传递给 superDog(String name,this.breed):super(name);}
classDogextendsAnimal{finalString breed;// 新方式:使用 super-initializer 参数Dog(super.name,this.breed);// ↑ super.name 自动将 name 参数转发给父类 Animal 构造函数}

下面是个错误案例

Vector3d.xAxisError(super.x):z=0,super(0);// BAD

这个命名构造函数试图设置x的值两次:一次是在super构造函数中,另一次是作为位置超参数。因为它们都指向位置参数x,所以会导致错误。

当超类构造函数有命名参数时,你可以将它们分配到:
命名的 super 参数(如下例中的 super.y)
超类构造函数调用中的命名参数(如 super.named(x: 0))

classVector2d{// ...Vector2d.named({requiredthis.x,requiredthis.y});}classVector3dextendsVector2d{finaldouble z;// Forward the y parameter to the named super constructor like:// Vector3d.yzPlane({required double y, required this.z})// : super.named(x: 0, y: y);Vector3d.yzPlane({requiredsuper.y,requiredthis.z}):super.named(x:0);}

小结

太灵活了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 8:02:14

快速预览远程文件:QuickLook如何让FTP/SFTP预览体验与本地文件无异

快速预览远程文件&#xff1a;QuickLook如何让FTP/SFTP预览体验与本地文件无异 【免费下载链接】QuickLook 项目地址: https://gitcode.com/gh_mirrors/qui/QuickLook 还在为查看服务器上的单个文件而被迫下载整个压缩包&#xff1f;是否经历过因网络延迟导致的文件预览…

作者头像 李华
网站建设 2026/4/18 8:36:00

GoCV实战:从图像处理到界面集成的完整方案

GoCV实战&#xff1a;从图像处理到界面集成的完整方案 【免费下载链接】gocv hybridgroup/gocv: 是一个基于 Go 语言的开源计算机视觉库&#xff0c;支持多种计算机视觉算法和工具。该项目提供了一个简单易用的计算机视觉库&#xff0c;可以方便地实现图像和视频处理算法&#…

作者头像 李华
网站建设 2026/4/18 5:19:59

Apache Kvrocks完整部署指南:3步搭建高性能Redis替代方案

Apache Kvrocks完整部署指南&#xff1a;3步搭建高性能Redis替代方案 【免费下载链接】kvrocks Apache Kvrocks is a distributed key value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol. 项目地址: https://gitcode.com/gh_…

作者头像 李华
网站建设 2026/4/18 7:04:20

YOLOv9模型评估实战:从指标困惑到性能洞察的完整指南

YOLOv9模型评估实战&#xff1a;从指标困惑到性能洞察的完整指南 【免费下载链接】yolov9 项目地址: https://gitcode.com/GitHub_Trending/yo/yolov9 你是否曾经面对一堆COCO评估指标却不知从何入手&#xff1f;当mAP0.5和mAP0.5:0.95同时出现时&#xff0c;是否感到困…

作者头像 李华
网站建设 2026/4/18 8:21:48

数据编排革命:Apache DolphinScheduler让ETL流程管理变得如此简单

数据编排革命&#xff1a;Apache DolphinScheduler让ETL流程管理变得如此简单 【免费下载链接】dolphinscheduler 项目地址: https://gitcode.com/gh_mirrors/ea/EasyScheduler 在当今数据驱动的时代&#xff0c;企业面临着海量数据处理和复杂ETL流程管理的巨大挑战。A…

作者头像 李华