十三、Dart中的范型
13.1 范型方法
通俗理解: 范型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)
案例1:
- 定义一个函数只能返回String类型的数据
String getStringData(String value){return value;
}
案例2:
- 同时支持返回String和int类型
String getStrData(String value){return value;
}
int getIntData(int value){return value;
}
案例3: 同时返回String类型和number类型,不指定类型可以解决这个问题
getMixData(value){return value;
}
案例4:
- 不指定类型放弃了类型检查.我们现在想实现的是传入什么,返回什么.
- 比如传入number,类型必须返回number类型,传入String类型必须返回String类型.要求类型一致.
- 因此引入范型
- 用T对返回值类型进行校验
- 对返回值类型不进行校验
- 因此引入范型
//T校验返回值T getMixDatas<T>(T value){return value;}//不校验返回值getMixDatal<T>(T value){return value;}
-
- 案例调用
main(){print(getMixDatas(2222));print(getMixDatas("11111"));
}
-
- 但是这么操作还是没有实现类型一致性,既然引入了范型,那么我们在调用的时候添加上约束就行
print(getMixDatas<String>("3333"));
print(getMixDatas<int>(4444));
13.2 范型在类中的用法.
- 集合List在范型类的用法
案例:
把下面类转换成范型类,要求MyList里面可以增加int类型的数据,也可以增加String类型的数据.但是每次调用增加的类型要统一.
class MyList{List list = <int>[];void add(int value){this.list.add(value); }List getList(){return list; }
}
main(){MyList l = new MyList();l.add(1);l.add(12);l.add(5);print(l.getList());//[1,12,5]
}
-
- 修改后的实现部分
class MyList<T>{List list = <T>[];void add(T value){this.list.add(value); }List getList(){return list; }
}
-
- 案例调用
List list1 = new List<int>.filled(2,0);//初始化list1列表,长度为2,填充值为0,也就是[0,0]
list1[0] = 11111;
list1[1] = 22222;
print(list1);//[11111, 22222]
13.3 范型接口
案例1:
-
- 两个抽象类ObjectCache、StringCache
- 对两个抽象类进行复用性精简.
abstract class ObjectCache {getByKey(String key);void setByKey(String key,Object value);
}
abstract class StringCache{getByKey(String key);void setByKey(String key,String value);
}
//将上述两个抽象类进行复用性精简
abstract class Cache<T> {getByKey(String key);void setByKey(String key,T value);
}
案例2:
实现数据缓存的功能: 有文件缓存和内存缓存.内存缓存和文件缓存按照接口约束实现.
-
- 1、定义一个范型接口,约束实现它的子类必须有getByKey(key)和setByKey(key,value)
- 2、要求setByKey的时候的value类型和实例化子类的时候指定的类型一致.
根据要求,首先需要定义两种类型的缓存,通过对缓存抽象类的实现,定制两种类型的类.
abstract class Cache<T>{getByKey(String key);void setByKey(String key,T value);
}
class FileCache<T> implements Cache<T>{@overridegetByKey(String key){return null; }@overridevoid setByKey(String key,T value){print("我是文件缓存 把key=${key} value=${value}的数据写入到了文件中"); }
}
class MemoryCache<T> implements Cache<T>{@override getByKey(String key){return null; }@overridevoid setByKey(String key,T value){print("我是内存缓存 把key=${key} value=${value} - 写入到了内存中"); }
}
- 根据编写的类,对其进行调用测试
void main(){MemoryCache m = new MemoryCache<String>();m.setByKey('index','首页数据');//我是内存缓存 把key=index value=首页数据 -写入到了内存中MemoryCache n = new MemoryCache<Map>();n.setByKey('index',{"name": "张三", "age": 20});//我是内存缓存 把key=index value={name: 张三, age: 20} -写入到了内存中
}
13.4 总结
- 范型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)
十四、Dart中的库
14.1 Dart中的库
前面介绍Dart基础知识的时候基本上都是在一个文件里面编写Dart代码,但实际开发中不可能这么写,模块化很重要,所以这就需要使用到库的概念.
- 在Dart中,库的使用是通过import关键字引入的.
- library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定.
Dart中的库主要有三种:
- 我们自定义的库
- import 'lib/xxx.dart';
- 系统内置库
- import 'dart:math';
- import 'dart:io';
- import 'dart:convert';
- Pub包管理系统中的库
- Page 1 | Top packages、Page 1 | Top packages、Search results for sdk:flutter
- 需要在自己项目根目录下新建一个pubspec.yaml
- 在pubspec.yaml文件,然后配置名称,描述,依赖等信息.
- 然后运行pub get 获取包下载到本地
- 项目中引入库 import 'package:http.dart' as http;
例如: 一个pubspec.yaml文件
name: xxx
description: A new flutter module project.
dependencies:http: ^0.12.0+2date_format: ^1.0.6
environment:sdk: '>=2.10.0 <3.0.0'
14.2 Dart中导入自己本地库
在一个模块中导入自己本地库,也就是两个文件的调用关系,比如在lib文件夹下创建的Animal.dart模块,在main.dart模块中需要使用,那么只需要给相对路径即可.
import 'lib/Animal.dart';
main(){var a=new Animal('小黑狗', 20);print(a.getName());
}
14.3 导入系统内置库
如果我们想要使用系统库,系统库的前缀是 'dart:',在它的后面加上你要使用的哪个系统库.command+鼠标左键即可点进去一个库对其内容进行查看.
import 'dart:math';
main(){print(min(12,23)); //12print(max(12,25)); //25
}
14.4 导入系统内置库实现请求数据
一个网络数据的请求过程:
- 创建HttpClient对象
- 创建url对象
- 发起请求,等待请求
- 关闭请求,等待响应
- 解码响应的内容
案例: 请求知乎最近的新闻数据
- HttpClient是系统内置库io中的模块
- 需要用到数据解析,使用的是convert中相关的模块
import 'dart:io';
import 'dart:convert';
void main() async{var result = await getDataFromZhihuAPI();print(result);
}
getDataFromZhihuAPI() async {//1、创建HttpClient对象var httpClient = new HttpClient();//2、创建Url对象var uri = new Uri.http('news-at-zhihu.com','/api/3/stories/latest');//3、发起请求,等待请求var request = await httpClient.getUrl(url);//4、关闭请求,等待响应var response = await request.close();//5、解码响应的内容return await response.transform(utf8.decoder).join();
}
- 执行函数,返回请求结果如下:
{"date":"20230501","stories":[{"image_hue":"0x424f37","title":"小事 · 你见过哪些父母惊艳到你的教育方式?","url":"https:\/\/daily.zhihu.com\/story\/9761023","hint":"VOL.1483","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-4b3cb2892a38b2340b12ba33c6ff381a.jpg?source=8673f162"],"type":0,"id":9761023},{"image_hue":"0x453040","title":"有哪些知道了语源后会令人感到惊讶的英语单词?","url":"https:\/\/daily.zhihu.com\/story\/9760986","hint":"一直在路上David · 4 分钟阅读","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-f66107a7770e06616a689d6e542d2136.jpg?source=8673f162"],"type":0,"id":9760986},{"image_hue":"0xb37d7d","title":"吕秀才和郭芙蓉在一起是不是郭巨侠祝无双所认为的一时冲动?","url":"https:\/\/daily.zhihu.com\/story\/9760990","hint":"童大掌柜呢 · 8 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-889394ae433b0996ea24f6a6129d1ae8.jpg?source=8673f162"],"type":0,"id":9760990},{"image_hue":"0x547bae","title":"燕子为何会选择在人类屋檐下搭窝?","url":"https:\/\/daily.zhihu.com\/story\/9761000","hint":"知乎用户 · 2 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-ba775f62ab095a62d3c7ab512f68fba5.jpg?source=8673f162"],"type":0,"id":9761000},{"image_hue":"0x040404","title":"世界上有哪些两地相隔不远但互通必须绕一大圈的例子?","url":"https:\/\/daily.zhihu.com\/story\/9761008","hint":"莱茵行宫伯爵 · 2 分钟阅读","ga_prefix":"050107","images":["https:\/\/pic1.zhimg.com\/v2-368438800a90e5f1bdd081f3e9e1c351.jpg?source=8673f162"],"type":0,"id":9761008},{"image_hue":"0x7c643f","title":"面粉为什么能洗出面筋?粉是怎么转化成「筋」的呢?","url":"https:\/\/daily.zhihu.com\/story\/9761018","hint":"钱程 · 1 分钟阅读","ga_prefix":"050107","images":["https:\/\/picx.zhimg.com\/v2-d798a6d979126c6ce16f573daced0ee1.jpg?source=8673f162"],"type":0,"id":9761018}],"top_stories":[{"image_hue":"0x424f37","hint":"作者 \/ 文小白","url":"https:\/\/daily.zhihu.com\/story\/9761023","image":"https:\/\/picx.zhimg.com\/v2-d412fe4c9e66a9c2aaed7ad75fa6b975.jpg?source=8673f162","title":"小事 · 你见过哪些父母惊艳到你的教育方式?","ga_prefix":"050107","type":0,"id":9761023},{"image_hue":"0xafafaf","hint":"作者 \/ 真实故事计划","url":"https:\/\/daily.zhihu.com\/story\/9760980","image":"https:\/\/picx.zhimg.com\/v2-f01dedf1b9345156c61204fa3724b9ed.jpg?source=8673f162","title":"小事 · 你见过的最阴暗的事是什么?","ga_prefix":"043007","type":0,"id":9760980},{"image_hue":"0x5e6c24","hint":"作者 \/ 百花杀","url":"https:\/\/daily.zhihu.com\/story\/9760967","image":"https:\/\/pic1.zhimg.com\/v2-3263de81e79d67126d92b379ab6a5cb8.jpg?source=8673f162","title":"小事 · 你遇过的最温暖的瞬间是什么?","ga_prefix":"042907","type":0,"id":9760967},{"image_hue":"0xb39168","hint":"作者 \/ 斜绿天蛾","url":"https:\/\/daily.zhihu.com\/story\/9760874","image":"https:\/\/pic1.zhimg.com\/v2-a4bc6aa6a1c1ba806e8306d27adc8adc.jpg?source=8673f162","title":"昆虫演化真的符合进化论吗?","ga_prefix":"042807","type":0,"id":9760874},{"image_hue":"0x946d68","hint":"作者 \/ 于朝阳","url":"https:\/\/daily.zhihu.com\/story\/9760851","image":"https:\/\/pic1.zhimg.com\/v2-c95731f8815aadd68e2e7cb5c518ff4c.jpg?source=8673f162","title":"秦朝(秦国)为何迅速灭亡?","ga_prefix":"042
14.4 关于async和await
这两个关键字的使用只需要记住两点:
- 只有async方法才能使用await关键字调用方法
- 如果调用别的async方法必须使用await关键字
async是让方法变成异步.
await是等待异步方法执行完成.
void main() async {var result await testAsync();print(result);
}
testAsync() async {return "Hello async";
}
14.5 Dart导入pub包管理系统中的库
14.5.1 pub包管理系统简介:
1、从下面网址找到要用的库
- Page 1 | Top packages
- Page 1 | Top packages
- Search results for sdk:flutter
2、创建一个pubspec.yaml文件,内容如下
name: xxxdescription: A new flutter module project.dependencies: http: ^0.12.0+2date_format: ^1.0.6
3、配置dependencies
4、运行pub get 获取远程库
5、看文档引入库使用
14.5.2 案例: 网络请求及响应
- 首先导入需要用到的模块、数据解析用到 'dart:convert'
- http请求使用了 package:http/http.dart
- 时间格式化采用 date_format.dart
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
- 实现部分
main() async {var url = "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";var response = await http.get(url);if response.statusCode == 200 {var jsonResponse = convert.jsonDecode(response.body);print(jsonResponse); } else {print("Request failed with status: ${response.statusCode}."); }print(formatDate(DateTime(2023,5,1), [yyyy,'**',mm,"**",dd])); //2023**05**01
}
14.6 Dart 库的重命名&Dart冲突解决
1、冲突解决
- 当引入两个库中有相同名称标识符的时候
- 如果是Java通常我们通过写上完整的包名称路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的.
- 当冲突的时候,可以使用 as关键字来制定库的前缀.
- 例如:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element();// Uses Element from lib2.
案例:
import 'lib/Person1.dart';
import 'lib/Person2.dart' as lib;
main(List<String> args) {Person p1=new Person('张三', 20);p1.printInfo(); //Person1:张三----20lib.Person p2=new lib.Person('李四', 20);p2.printInfo(); //Person2:李四----20
}
14.7 部分导入
部分导入:
- 如果只需要导入库的一部分,有两种模式
- 一、只导入需要的部分,使用show关键字,如下所示
import 'package:lib1/lib1.dart
-
- 二、隐藏不需要的部分,使用 hide 关键字, 如下所示
import 'package:lib2/lib2.dart' hide foo;
案例:
- 实现这么一个模块,名叫myMath.dart
void getName(){print('张三');
}
void getAge(){print(20);
}
void getSex(){print('男');
}
- 在使用时
- 1、如果只想用 getAge方法,那么
import 'lib/myMath.dart' show getAge;
void main(){getAge();
}
-
- 2、如果想要隐藏 getName方法,那么调用就会报错
import 'lib/myMath.dart' hide getName;
void main(){
// getName();//调用报错getAge();
}
14.8 延迟加载
- 也称为懒加载,可以在需要的时候再进行加载
- 懒加载的最大好处是可以减少App的启动时间
- 懒加载使用deferred as关键字来指定,如
import 'package:deferred/hello.dart' deferred as hello;
- 当需要使用的时候,需要使用loadLibrary()方法来加载;
greet() async {await hello.loadLibrary();hello.printGreeting();
}
14.9 总结
1、Dart中的库主要有三种:
- 我们自定义的库
- import 'lib/xxx.dart';
- 系统内置库
- import 'dart:math';
- import 'dart:io';
- import 'dart:convert';
- Pub包管理系统中的库
- Page 1 | Top packages、Page 1 | Top packages、Search results for sdk:flutter
- 需要在自己项目根目录下新建一个pubspec.yaml
- 在pubspec.yaml文件,然后配置名称,描述,依赖等信息.
- 然后运行pub get 获取包下载到本地
- 项目中引入库 import 'package:http.dart' as http;
2、在一个模块中导入自己本地库,也就是两个文件的调用关系,比如在lib文件夹下创建的Animal.dart模块,在main.dart模块中需要使用,那么只需要给相对路径即可.
3、如果我们想要使用系统库,系统库的前缀是 'dart:',在它的后面加上你要使用的哪个系统库.
4、async和await这两个关键字的使用只需要记住两点:
-
- 只有async方法才能使用await关键字调用方法
- 如果调用别的async方法必须使用await关键字
- async是让方法变成异步.
- await是等待异步方法执行完成.
5、Dart导入pub包管理系统中的库
1、从下面网址找到要用的库
-
- Page 1 | Top packages
- Page 1 | Top packages
- Search results for sdk:flutter
2、创建一个pubspec.yaml文件
3、配置dependencies
4、运行pub get 获取远程库
5、看文档引入库使用
6、冲突解决
- 当引入两个库中有相同名称标识符的时候
- 如果是Java通常我们通过写上完整的包名称路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的.
- 当冲突的时候,可以使用 as关键字来制定库的前缀.
7、如果只需要导入库的一部分,有两种模式
-
- 一、只导入需要的部分,使用show关键字
- 二、隐藏不需要的部分,使用 hide 关键字
8、延迟加载
- 也称为懒加载,可以在需要的时候再进行加载
- 懒加载的最大好处是可以减少App的启动时间
- 懒加载使用deferred as关键字来指定
十五、Dart 2.13后的新特性 Null safety、late关键字、空类型声明符?、非空断言!、required关键字
15.1 Null safety & 类型断言
Null safety翻译成中文的意思是空安全。
null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。
Flutter2.2.0(2021年5月19日发布) 之后的版本都要求使用null safety。
- ? 可空类型(相当于Swfit中的可选值Optional)
- ! 类型断言(相当于Swift中的强制解包)
案例:
String? getData(apiUrl){if(apiUrl !=null ){return "this is server data";}return null;
}
- 案例调用
//1、null safetyint a=123;print(a);//123String username="张三";print(username);//张三List<String> l1=["张三","李四","王五"];print(l1);//[张三, 李四, 王五]// int b=123; //非空的int类型// b=null; //A value of type 'Null' can't be assigned to a variable of type 'int'// String usernameA="张三"; //非空的String类型// usernameA=null; //A value of type 'Null' can't be assigned to a variable of type 'String'.String? usernameB="张三"; // String? 表示username是一个可空类型usernameB=null;print(usernameB);//nullint? c=123; // int? 表示a是一个可空类型c=null; print(c);//nullList<String> l2=["张三","李四","王五"];// l2=null; //A value of type 'Null' can't be assigned to a variable of type 'List<String>'.List<String>? l3=["张三","李四","王五"];l3=null; print(l3);//null//调用方法print(getData("http://www.itying.com"));//this is server dataprint(getData(null)); //null
//2、! 类型断言String? str="this is str";str=null;print(str!.length); //null// 类型断言: 如果str不等于null 会打印str的长度,如果等于null会抛出异常printLength("str");//printLength(null);
15.2 late关键字
- late关键字主要用于延迟初始化
案例:
- 声明构造方法时,要求初始化成员不能为空,于是得加上late懒加载修饰
class Person {late String name;late int age;void setName(String name, int age) {this.name = name;this.age = age;}String getName() {return "${this.name}---${this.age}";}
}
void main(args) {Person p = new Person();p.setName("张三", 20);print(p.getName());
}
15.3 late接口
接口就是约定、规范
abstract class DB { //接口: 约定、规范late String uri; //数据库的链接地址add(String data);save();delete();
}
- 在定义抽象类时,如果添加了成员变量,未初始化的情况下就会报错,这个时候我们使用late修饰,懒加载执行.
15.4 required关键词
required关键词
- 最开始@required是注解; 现在它已经作为内置修饰符
- 主要用于允许根据需要标记任何命名参数(函数和类),使得它们不为空.因为可选参数中必须有个 required参数或者该参数有个默认值.
案例1:
- 如果想要age和sex可以为空,那么我们这样表示
String printUserInfo(String username, {int age=10, String sex="男"}) {//行参 return "姓名:$username---性别:$sex--年龄:$age";
}
案例2:
- 如果想让age和sex必须有值,那么添加关键字required
String printInfo(String username, {required int age, required String sex}) {//行参 return "姓名:$username---性别:$sex--年龄:$age";
}
- 案例调用
void main(args) {print(printUserInfo('张三'));print(printUserInfo('张三',age: 20,sex: "女"));//age 和 sex必须传入print(printInfo('张三',age: 22,sex: "女"));
}
15.5 required命名参数
案例:
- 在定义一个类时,我们想让构造方法中的参数作为必须传入的命名参数时,采用required修饰
//表示 name 和age 是必须传入的命名参数
class Person {String name;int age;Person({required this.name,required this.age}); //表示 name 和age 必须传入String getName() {return "${this.name}---${this.age}";}
}
- 案例调用
void main(args) {Person p=new Person(name: "张三",age: 20);print(p.getName());
}
15.6 required命名参数可选
- 在定义一个类,构造函数中的参数,我们想让一部分可以传入也可以不传入,另一部分必须传入,采用可控类型修饰.
案例:
- name可以传入也可以不传入,age必须传入
class Person {String? name;//可空属性int age;Person({this.name, required this.age});//表示name可空,age必须传入.String getName(){return "${this.name} --- ${this.age}"; }
}
- 案例调用
void main(args) {Person p=new Person(name: "张三",age: 20);print(p.getName()); //张三---20Person p1=new Person( age: 20);print(p1.getName()); //null---20
}
15.7 总结
- null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。
- ? 可空类型(相当于Swfit中的可选值Optional)
- ! 类型断言(相当于Swift中的强制解包)
- late关键字主要用于延迟初始化.声明构造方法时,要求初始化成员不能为空,于是得加上late懒加载修饰.
- required关键词
- 最开始@required是注解; 现在它已经作为内置修饰符
- 主要用于允许根据需要标记任何命名参数(函数和类),使得它们不为空.因为可选参数中必须有个 required参数或者该参数有个默认值.
- 在定义一个类,构造函数中的参数,我们想让一部分可以传入也可以不传入,另一部分必须传入,采用可控类型修饰.
十六、Dart性能优化之常量、identical函数、常量构造函数详解
16.1 final和const修饰符
Dart常量用: final和const修饰符修饰.
- const声明的常量是在编译时确定的,永远不会改变.
- final声明的常量允许声明后再赋值,赋值后不可改变,final声明的变量是在运行时确定的.
- final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
final和const区别:
- final可以开始不赋值,只能赋一次;而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
//const常量
const PI=3.14;
//PI=3.14159; //const定义的常量没法改变
print(PI);
// final 常量
final a;a = 13;
// a=14; //不能再重新赋值print(a);final d = new DateTime.now();print(d);//2023-05-01 14:16:39.145643
16.2 Dart中const identical函数
dart:core 库中identical函数的用法介绍
- 用法:
bool identical(Object? a,Object? b
)
- 检查两个引用是否指向同一个对象
var o = new Object();
var isIdentical = identical(o,new Object());//false, different objects
print(isIdentical);
isIdentical = identical(o,o);// true , same object
print(isIdentical);
isIdentical = identical(const Object(),const Object());//true, const canonicalizes 规范化
print(isIdentical);
isIdentical = identical([1], [1]); // false
print(isIdentical);
isIdentical = identical(const [1], const [1]); // true
print(isIdentical);
isIdentical = identical(const [1], const [2]); // false
print(isIdentical);
isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
print(isIdentical);
案例1: identical对两个对象的引用检查
var o1 = new Object();var o2 = new Object();print(identical(o1,o2)); //false 不共享存储空间print(identical(o1,o1)); //true 共享存储空间
案例2: 采用const修饰的常量构造函数通过identical引用检查
//const Object()表示实例化常量构造函数//o1 和 o2共享了存储空间var o1 = const Object();var o2 = const Object();print(identical(o1,o2)); //true 共享存储空间print(identical(o1,o1)); //true 共享存储空间
案例3: 两个相同的List列表
var a=[2];var b=[2];print(identical(a,b)); //false 不共享存储空间
案例4: const修饰的两个相同元素的List列表
print(identical(const [2],const [2])); //true
const a=[2];
const b=[2];
print(identical(a,b)); //true 共享存储空间
案例5: const修饰的两个不同元素的List列表
const c=[2];const d=[3];print(identical(c,d)); //false 不共享存储空间
- 因此: const关键词在多个地方创建相同的对象的时候,内存中只保留了一个对象
- 共享存储空间条件: 1、常量 2、值相等.
16.3 Dart普通构造函数
案例:
class Container{int width;int height;Container({required this.width,required this.height});
}
void main(){var c1 = new Container(width: 100, height: 100);var c2 = new Container(width: 100, height: 100);print(identical(c1,c2));//false, c1和c2在内存中存储了2份
}
16.4 常量构造函数
常量构造函数总结如下:
- 常量构造函数需以const关键字修饰
- const构造函数必须用于成员变量都是final的类
- 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
- 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.
- Flutter中const修饰不仅仅是节省组件构建时的内存开销,Flutter在需要重新构建组件的时候,由于这个组件不应该改变的,重新构建没有任何意义,因此Flutter不会重新构建const组件.
案例: 常量构造函数
class Container {final int width;final int height;const Container({required this.width,required this.height});
}
- 案例调用
var c1 = Container(width: 100, height: 100);
var c2 = Container(width: 100, height: 100);
print(identical(c1,c2));//false
var c3 = const Container(width:100,height:100);
var c4 = const Container(width:100,height:100);
print(identical(c3,c4));//true
var c5 = const Container(width:100,height:100);
var c6 = const Container(width:100,height:100);
print(identical(c5,c6));//false
- 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.
16.5 总结
- Dart常量用: final和const修饰符修饰.
- const声明的常量是在编译时确定的,永远不会改变.
- final声明的常量允许声明后再赋值,赋值后不可改变,final声明的变量是在运行时确定的.
- final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
- final和const区别:
- final可以开始不赋值,只能赋一次;而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化.
- identical: 检查两个引用是否指向同一个对象
- 常量构造函数总结如下:
- 常量构造函数需以const关键字修饰
- const构造函数必须用于成员变量都是final的类
- 如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
- 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.
- Flutter中const修饰不仅仅是节省组件构建时的内存开销,Flutter在需要重新构建组件的时候,由于这个组件不应该改变的,重新构建没有任何意义,因此Flutter不会重新构建const组件.
- 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象.