类与对象
Dart 类
- 类是通过 class 声明的代码段,包含属性和方法
- 属性: 用来描述类的变量
- 方法: 类中的函数称为类的方法
- 对象是类的 实例化 结果
var obj = new MyClass();
- 编程方式
- 面向对象编程 (OOP)
- 面向过程编程(函数编程) (POP)
dart
void main(){
// 实例化类,得到一个对象
Person p = new Person();
// 访问类中的属性
print(p.name); // result:张三
// 访问类中的方法
p.getInfo(); // result:我的名字是: 张三
// Dart 中所有的内容都是对象
Map m = new Map(); // Map本质也是类,Dart 内置的类
print(m.length); // result:0
}
// 声明类
class Person{
// 类的属性
String name = '张三';
// 类的方法
void getInfo(){
print('我的名字是 $name');
}
}
构造器(构造函数)
- 默认构造函数: 与类同名的函数,在实例化时,自动被调用
- 命名构造函数: 在类中使用命名构造器( 类名.函数名 )实现多个构造器,可以提供额外的清晰度
- 常量构造函数: 如果类生成的对象不会改变,那就可以通过常量构造函数使这些对象成为编译时常量
- 工厂构造函数: 通过 factory 声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例
默认构造函数
dart
// 默认构造函数
void main(){
Point p = new Point(); // result: 我是默认构造函数,实例化时会第一个被调用
print('${p.x},${p.y}'); // result: 0,null
}
class Point{
num? x,y;
// 声明普通构造函数
Point(){
print('我是默认构造函数,实例化时会第一个被调用');
// this可以省略,但是当命名指向有歧义时,this 不能省略(比如参数是x,类里面又定义x,两者是不一样的)
this.x=0;
}
}
dart
// 带参默认构造函数,如果默认构造函数带参数,那么实例化类的时候必须带参数
void main() {
Point p = new Point(1, 2);
print('${p.x},${p.y}'); // result: 1,2
}
class Point {
num? x, y;
// 声明带参数的普通构造函数,简写: Point(this.x,this.y);
Point(x, y) {
this.x = x;
this.y = y;
}
}
命名构造函数
dart
// 命名构造函数
void main() {
Point p1 = Point(10, 20);
print('${p1.x}, ${p1.y}'); // result:10,20
Point p2 = Point.origin();
print('${p2.x}, ${p2.y}'); // result:0,0
Point p3 = Point.fromJson();
print('${p3.x}, ${p3.y}'); // result:0,1 ,不传参数的时候,默认值设定的是x=0,y=1
Point p4 = Point.fromJson(x: 100, y: 200);
print('${p4.x}, ${p4.y}'); // result:100,200
}
class Point {
num? x, y;
Point(this.x, this.y);
// 命名构造函数,简写方式: Point.origin() : x = 0, y = 0;
Point.origin() {
this.x = 0;
this.y = 0;
}
// 命名构造函数fromJson,默认x,y的参数时0,1. 简写方式: Point.fromJson({x = 0, y = 1}) : x = x, y = y;
Point.fromJson({x = 0, y = 1}) {
this.x = x;
this.y = y;
}
}
常量构造函数
dart
// 常量构造函数
void main() {
Point p1 = Point(1, 2);
Point p2 = Point(1, 2);
print(p1 == p2); // result: false,因为在内存当中地址并不一样
// 常量构造函数,可以当作普通构造函数使用
ImmutablePoint ip1 = ImmutablePoint(1, 2);
ImmutablePoint ip2 = ImmutablePoint(1, 2);
print(ip1 == ip2); // result: false
// 声明不可变对象,必须通过 const 关键字
ImmutablePoint ip3 = const ImmutablePoint(1, 2);
ImmutablePoint ip4 = const ImmutablePoint(1, 2);
print(ip3 == ip4); // result: true,使用 const 声明的常量构造函数,只要参数是一致的,它们就相等.
}
class Point {
num? x, y;
Point(this.x, this.y);
}
//常量构造函数
class ImmutablePoint {
// 属性必须通过 final 声明
final num x;
final num y;
// 常量构造函数,必须通过 const 声明
const ImmutablePoint(this.x, this.y);
}
工厂构造函数
dart
void main() {
Person p1 = Person('关羽');
print(p1.name); // result:关羽
Person p2 = Person('张飞');
print(p2.name); // result:关羽,因为p1已经实例化过了
print(p1 == p2); // result: true
}
class Person {
String? name;
static Person? instance; // instance 是静态变量,Person类的静态属性,使用static修饰
// 工厂构造函数
factory Person([String name = '刘备']) {
// 工厂构造函数中,不能使用 this 关键字,因为它不会自动生成实例
// print(this.name); //这样写不允许的
if (Person.instance == null) {
// 判断instance是不是第一次实例化,假如第一次调用就赋值
Person.instance = Person.newSelf(name);
}
// 返回现存的实例,使用 ! 断言不为null
return Person.instance!;
}
// 命名构造函数
Person.newSelf(this.name);
}
访问修饰
- Dart 与 TypeScript 不同,没有访问修饰符(public、protected、private)
- Dart 类中,默认的访问修饰符是公开的(即 public)
- 如果 属性 或 方法 以
_
(下划线)开头,则表示私有(即 private) - 只有把类单独抽离出去,私有属性和方法才起作用
dart
void main(){
Person p = Person('张三');
print(p.name); // result:张三
// 访问私有属性
print(p._money); // result:100,因为Person类和main函数在同一个文件中,所以可以访问
}
class Person{
String? name;
// 声明私有属性
num _money=100;
Person(this.name);
}
文件结构
├─ lib
│ └─ Person.dart // Person 类
└─ main.dart // 程序运行入口
dart
// 文件目录和lib同级的main方法入口
import './lib/Person.dart';
void main(){
Person p = Person('张三');
print(p.name); // result:张三
// 访问私有属性
print(p._money); // result:Error,此时不能访问Person类里面的私有属性
print(p.getMoney()); // result: 100
// 访问私有方法
p._wife(); // result:Error,无法访问Person类里面的私有方法
}
dart
// 文件目录在lib文件下的Person.dart
class Person{
String? name;
// 声明私有属性
num _money=100;
Person(this.name);
//设定一个公共的方法,就可以通过公共的方法访问_money这个Person类的私有属性
num getMoney(){
return this._money;
}
// 声明私有方法
void _wife() {
print('我是 $name 的妻子');
}
}
Getter 与 Setter
- Getter(获取器): 是通过 get 关键字修饰的方法
- 函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)
- Setter(修改器): 是通过 set 关键字修饰的方法
- 访问时,像设置属性一样给函数传参
dart
void main() {
Circle c = Circle(10);
print(c.area()); // result:314.15926...
// 使用 get 声明的方法,访问方法就像访问属性一样,不需要带括号
print(c.getArea); // result:314.15926...
// 通过 Setter 修改属性
c.setR = 20; // 通过 set 声明的方法,赋值就像属性一样赋值
print(c.getArea);
}
// 圆的类
class Circle {
final double PI = 3.1415926; // 𝛑 圆周率
num? r; // 圆的半径
Circle(this.r);
// 返回圆的面积
num area() {
return PI * r! * r!;
}
// Getter,返回圆的面积,使用 get 声明的方法,不能有小括号
num get getArea {
return PI * r! * r!;
}
// Setter,重新设置圆的半径
set setR(val) {
r = val;
}
}
类的默认属性和方法
call
- 在类中可以声明 call 方法 (方法名不能变)
- 当我们将类的实例, 用作函数时, 会自动调用 call 方法
noSuchMethod
- 当我们调用了一个类的未定义的方法时,Dart 会自动调用 noSuchMethod
- 使用前提
- 类中声明了 noSuchMethod (否则调用默认的 noSuchMethod)
- 实例化对象必须用 dynamic 来修饰
dynamic p = Person;
- 调用的是未定义方法 (p.undefinedMethod());
dart
void main() {
dynamic p = Person(); // 使用 noSuchMethod 方法,实例化对象时候必须使用 dynamic 声明
p.say(); // result:Say something
// 虽然 Person 类没有定义 study 方法,但是因为 Person 类重写了 noSuchMethod 方法,所以可以正常使用不报错
p.study(); // result: Say noSuchMethod
}
class Person {
say() {
print('Say something');
}
@override
noSuchMethod(Invocation invocation) {
return print('Say noSuchMethod');
}
}
hashCode
- hashCode 是 Dart 对象的唯一标识
- hashCode 表现为一串数字
- Dart 中的每个对象都有 hashCode
- 我们可以通过 hashCode 来判断两个对象是否相等
dart
void main() {
// 普通类实例化
Person p1 = Person();
Person p2 = Person();
print('${p1.hashCode}, ${p2.hashCode}'); // result: 每次生成都会生成一串新的随机数字
print(p1.hashCode == p2.hashCode); // result:false ,两个实例化对象是不同的
// 单例模式实例化
PersonSingle ps1 = PersonSingle();
PersonSingle ps2 = PersonSingle();
print('${ps1.hashCode}, ${ps2.hashCode}'); // result:
print(ps1.hashCode == ps2.hashCode); // result:false ,两个实例化对象是不同的
}
// 普通类
class Person {
say() {
print('Say something');
}
}
// 单例模式类
class PersonSingle {
// 静态私有实例变量
static PersonSingle? _instance;
// 私有命名构造函数
PersonSingle._internal();
// 提供公共的访问点
static PersonSingle getInstance() {
_instance ??= PersonSingle._internal();
return _instance!;
}
// 工厂构造函数使用 getInstance 返回实例
factory PersonSingle() => getInstance();
}
typedef
- typedef 可以用来自定义类型别名,目的是让程序的可读性更强
- 我们可以在声明泛型时,使用自定义的类型
- 语法
typedef function_name(parameters);
// 定义函数类型别名typedef variable_name = List<int>;
// 定义非函数类型别名
- 版本声明
- SDK 2.13 之前,typedef 仅限于函数类型
- SDK 2.13 之后,typedef 可以定义非函数类型
sdk:">=2.13.0"
dart
typedef MathOperation(int a,int b);
// 声明加法运算
add(int a,int b){
print('加法运算:' + (a + b).toString());
}
// 声明加法运算
sub(int a,int b){
print('减法运算:' + (a - b).toString());
}
add2(int a,int b,int c){
print('加法运算:' + (a + b +c).toString());
return a + b + c;
}
void main(){
print(add is MathOperation); // result:true,参数和 MathOperation 数量一样,参数类型也一样
print(add is Function); // result:true,add 是一个方法
print(sub is MathOperation); // result:true,参数和 MathOperation 数量一样,参数类型也一样
print(sub is Function); // result:true,sub 是一个方法
print(add2 is MathOperation); // result:false,参数和 MathOperation 数量不一样
print(add2 is Function); // result:true,add2 是一个方法
MathOperation op = add;
op(20,10); // result: 加法运算:30,
op = sub;
op(20,10); // result: 减法运算:10
// 计算器,typedef 的神奇用法
calculator(int a,int b ,MathOperation op){
op(a,b); // 这里加入op是add,那就执行加法运算,op是sub.那就执行减法运算
}
calculator(8,9,add); // result: 加法运算:17
calculator(8,9,sub); // result: 减法运算:-1
}
构造函数的初始化列表
- 作用: 在构造函数中设置属性的默认值
- 时机: 在构造函数体执行之前执行
- 语法: 使用逗号分隔初始化表达式
- 场景: 常用于设置 final 常量的值
dart
void main(){
Rect r = new Rect(); // result: 2 -- 10
}
class Rect{
int? height,width;
// 此种方式实例化 Rect 类时必须传值
<!-- Rect(this.height,this.width); -->
// 可选参数,初始化写法
<!-- Rect([int height = 2,int width = 10]){
this.height = height;
this.width = width;
print('${this.height} -- ${this.width}');
} -->
// 命名可选参数,初始化写法
<!-- Rect({int height = 2,int width = 10}){
this.height = height;
this.width = width;
print('${this.height} -- ${this.width}');
} -->
// 初始化列表写法,加不加this都可以
Rect()
: height = 2,
this.width = 10 {
print('${this.height} -- ${this.width}');
}
}
dart
// 初始化列表写法
void main() {
Point p1 = Point(1, 2, 3);
print(p1.z); // result: 3.0 ,因为z是double类型
Point p2 = Point.twoD(8, 9);
print(p2.z); // result: 0.0
}
class Point {
double x, y, z;
Point(this.x, this.y, this.z);
// 获取2d坐标,初始化列表的特殊用法(重定向构造函数),这里的this指向Point构造函数
Point.twoD(double x, double y) : this(x, y, 0);
}
static
- static 关键字用来指定静态成员
- 通过 static 修饰的属性是静态属性
- 通过 static 修饰的方法是静态方法
- 静态成员可以通过类名称直接访问(不需要实例化)
- 实例化是比较消耗资源的,声明静态成员,可以提高程序性能
- 静态方法不能访问非静态成员,非静态方法可以访问静态成员
- 静态方法中不能使用 this 关键字
- 不能使用 this 关键字,访问静态属性
dart
void main(){
// 静态成员,可以通过类名直接访问;不能通过类名直接访问非静态方法和属性
print(Person.name); // result: 张三
Person.printInfo(); // result: 张三
Person p = Person();
// print(p.name); // 不能通过实例化对象 p 去访问静态方法和属性
print(p.age); // result: 18
p.printUserInfo(); // result: 张三 18 张三
}
class Person{
// 静态属性
static String name = '张三';
int age = 18;
// 静态方法
static void printInfo(){
// print(this.name); // 不能通过this访问静态成员
print(name);
// print(age); // 静态方法不能访问非静态属性
// printUserInfo(); // 静态方法里面不能访问非静态方法
}
// 非静态方法可以访问静态属性
printUserInfo(){
print(name);
print(age);
printInfo(); // 非静态方法可以访问静态方法 // [!code default]
}
}
元数据
- 元数据以 @ 开头,可以给代码标记一些额外的信息
- 元数据可以写在库、类、构造器、函数、字段、参数或变量声明的前面
- @override (重写): 某方法添加该注解后, 表示重写了父类中的同名方法
- @required (必填): 可以通过 @required 来注解 Dart 中的命名参数,用来指示它是必填参数
- @deprecated (弃用): 若某类或某方法加上该注解之后,表示此方法或类不再建议使用
dart
void main(){
Phone p = Phone();
p.actived(); // result: 开机;能用,但是不赞成使用
p.turnOn(); // result: 开机
}
class Phone {
// 这是旧版本中的开机方法,会在将来的版本中移除
@deprecated('请使用turnOn替代actived')
actived() {
turnOn();
}
turnOn() {
print('开机');
}
}
继承 (extends)
- 根据类的先后顺序,可以将类分成父类和子类
- 子类通过 extends 关键字 继承 父类
- 继承后,子类可以使用父类中,可见的内容(属性或方法)
- 子类中,可以通过 @override 元数据来标记“重写”方法
- “重写”方法: 子类中与父类中同名的方法
- 子类中,可以通过 super 关键字来引用父类中,可见的内容
- 属性
- 方法(普通构造函数,命名构造函数)
文件结构
├─ lib
│ ├─ Father.dart
│ └─ Son.dart
└─ main.dart
继承的特性
dart
import 'lib/Father.dart'; // 引入Father类
import 'lib/Son.dart'; // 引入Son类
void main(){
Father f = Father();
print(f.name); // result:刘备
Son s = Son();
print(s.name); // result:刘备,因为Son继承父类,所以可以获取父类可见的name属性
print(s._money); // result:error,因为_money是father父类的私有属性,子类不能访问
print(s.getMoney); // result:10000, 子类可以通过另外的方式获取到父类 _money 的值
s.say(); // result:我是刘备,因为Son继承父类,所以可以获取父类可见的say方法
//第一句是子类引用了父类的target方法,同时因为子类重写了父类的target方法,所以子类实例化对象调用的是子类的方法.
s.target(); // result:刘备:我的目标是恢复汉室江山! 刘禅:我没有目标
}
dart
// 父类
class Father{
String name = "刘备";
int _money = 10000;
say(){
print('我是$name');
}
target(){
print('刘备:我的目标是恢复汉室江山!');
}
// 通过Getter写一个获取_money私有属性的方法
get getMoney => _money;
}
dart
import 'Father.dart'; //引入父类
// 子类,继承了Father父类
class Son extends Father {
@override
target(){
super.target(); // 引用了父类的方法
print('刘禅:我没有目标');
}
}
构造函数和命名构造函数的继承
dart
import 'lib/Father.dart'; // 引入Father类
import 'lib/Son.dart'; // 引入Son类
void main(){
Father f = Father('皇帝');
print(f.job); // result:皇帝
f.say(); // result: 我是刘备,我的职位是皇帝
Son s = Son('皇太子');
print(s.job); // result:皇太子
s.say(); // 我是刘禅,我的职位是皇太子,我父亲的职位是皇太子.注:这里父亲的job被由于被子类构造函数的传递,变成了皇太子
Son ss = Son.origin('卖草鞋');
ss.sayFather(); // result: 我是刘禅,我父亲原本的工作是卖草鞋.注:这里继承了父类的命名构造函数,并把参数传给了父类
}
dart
// 父类
class Father{
String name = "刘备";
String? job;
// 普通构造函数
Father(this.job);
// 命名构造函数
Father.origin(this.job);
say(){
print('我是$name,我的职位是$job');
}
}
dart
import 'Father.dart'; //引入父类
// 子类,继承了Father父类
class Son extends Father {
String name = "刘禅"; // 因为子类有父类的 name 属性,所以重写了 name
// 通过 super 继承了父类普通构造函数.注:父类写了有参构造函数,子类不写会报错
Son(String job) : super(job); // 初始化列表的写法,这句话相当于把子类的 job 参数传递给了父类的普通构造函数
// 继承父类的命名构造函数
// Son(String job) : super.origin(job); // 这种写法也没问题
Son.origin(String job) : super.origin(job);
@override
say(){
print('我是$name,我的职位是$job,我父亲的职位是${super.job}');
}
sayFather(){
print('我是$name,我父亲原本的工作是${super.job}');
}
}
抽象类 (abstract)
- 抽象类是用 abstract 关键字修饰的类
- 抽象类的作用是充当普通类的模板,约定一些必要的属性和方法
- 抽象方法是指没有方法体的方法
- 抽象类中一般都有抽象方法,也可以没有抽象方法
- 普通类中,不能有抽象方法
- 抽象类不能被实例化 (不能被 new)
- 抽象类可以被普通类继承 (extends)
- 如果普通类继承抽象类,必须实现抽象类中所有的抽象方法
- 抽象类还可以充当接口被实现 (implements)
- 如果把抽象类当作接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法
文件结构
├─ lib
│ ├─ Phone.dart // 手机抽象(abstract)类
│ ├─ IPhone.dart // 苹果手机类
│ └─ Samsung.dart // 三星手机类
└─ main.dart // 程序运行入口
dart
import './lib/IPhone.dart';
import './lib/Samsung.dart';
void main(){
// 抽象类不能被实例化
// Phone p = Phone();
IPhone i = IPhone();
i.processor(); // result: A18
i.camera(); // result: 1200万像素
Samsung s = Samsung();
s.processor(); // result: 高通 Gen 3
s.camera(); // result: 2000万像素
// Samsung 继承了 Phone ,可以使用 Phone 中的普通方法 info()
s.info(); // result: 我是抽象类中的一个普通方法
}
dart
// 1. 抽象类必须通过 abstract 关键字声明
// 2. 抽象类中可以有抽象方法,也可以没有抽象方法;但没有抽象方法的抽象类是没有意义的.
abstract class Phone{
// 声明抽象方法
void processor(); // 手机的处理器
void camera(); // 手机的摄像头
// 抽象类里面可以写普通方法
void info(){
print('我是抽象类中的一个普通方法');
}
}
dart
import 'Phone.dart';
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法
class IPhone extends Phone{
// 实现抽象类Phone里面的processor方法
@override
void processor(){
print('A18');
}
// 实现抽象类Phone里面的camera方法
@override
void camera(){
print('1200万像素');
}
// 普通类中不能有抽象方法,就是没有方法体的方法
// void screen();
}
dart
import 'Phone.dart';
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法
class Samsung extends Phone{
// 实现抽象类Phone里面的processor方法
@override
void processor(){
print('高通 Gen 3');
}
// 实现抽象类Phone里面的camera方法
@override
void camera(){
print('2000万像素');
}
}
接口 (Interface)
- 接口在 Dart 中就是一个类 (只是用法不同)
- 与 Java 不同, Java 中的接口需要用 interface 关键字声明; Dart 中不需要
- 接口可以是任意类,但一般使用抽象类做接口
- 一个类可以实现 (implements) 多个接口,多个接口用逗号分隔
class MyClass implements Interface1,Interface2 {...}
- 接口可以看成一个个小零件,类实现接口就相当于组装零件
- 普通类实现接口后,必须重写接口中所有的属性和方法
文件结构
├─ lib
│ ├─ Phone.dart // 手机具体类,实现 Processor,Camera 抽象类的接口
│ ├─ Processor.dart // 处理器抽象类
│ └─ Camera.dart // 摄像头抽象类
└─ main.dart // 程序运行入口
dart
import './lib/Phone.dart';
void main(){
Phone p = Phone('4核','1200万');
p.arch('5nm'); // result:芯片制程 5nm
p.brand('蔡司'); // result:相机品牌 蔡司
}
dart
import 'Processor.dart';
import 'Camera.dart';
// 通过普通类实现Processor、Camera接口
class Phone implements Processor, Camera{
// 必须重写接口的属性和方法
@override
String? cores;
@override
String? resolution;
// 构造函数
Phone(this.cores,this.resolution);
// 实现接口 Processor 里面的 arch 方法
@override
void arch(String? name){
print('芯片制程 $name');
}
// 实现接口 Camera 里面的 brand 方法
@override
void brand(String? name){
print('相机品牌 $name');
}
}
dart
// 抽象类,手机的处理器
abstract class Processor{
String? cores; // 处理器内核:2核,4核
arch(String name); // 芯片制程: 7nm,5nm
}
dart
// 抽象类,手机的摄像头
abstract class Camera{
String? resolution; // 像素:1000万,2000万
brand(String name); // 品牌:莱卡,蔡司
}
混入 (Mixin)
- 混入 (Mixin) 是一段公共代码,混入有两种声明方式:
- 将类当作混入
class MixinA {...}
- 作为 Mixin 的类只能继承自 Object ,不能继承其它类
- 作为 Mixin 的类不能有构造函数
- 使用 mixin 关键字声明
mixin MixinB {...}
- 将类当作混入
- 混入 (Mixin) 可以提高代码复用的效率, 普通类可以通过 with 来使用混入
class MyClass with MixinA, MixinB {...}
- 使用多个混入时, 后引入的混入会覆盖之前混入中的重复的内容
- 假定 MixinA 和 MixinB 中都有 hello() 方法, MyClass 会使用 MixinB 中的 hello() 方法
文件结构
├─ lib
│ ├─ MyClass.dart // 引用了 MixinA 和 MixinB 混入的普通类
│ ├─ MixinA.dart // 普通类声明的混入
│ └─ MixinB.dart // 使用 mixin 关键字声明的混入
└─ main.dart // 程序运行入口
dart
import './lib/MyClass.dart';
void main(){
MyClass m = MyClass();
m.printA(); // result:A
m.printB(); // result:B
// 后引入的混入,会覆盖之前引入的混入中重复的内容
print(m.name); // result:MixinB
m.run(); // result:B is runing
}
dart
import 'MixinA.dart';
import 'MixinB.dart';
// MyClass 引入了 MixinA 和 MixinB 混入
class MyClass with MixinA, MixinB{
}
dart
// 使用普通类声明的混入 MixinA
// MixinA 的类只能继承自 Object ,不能继承其它类,因为它被作为 Mixin 引用了
// MixinA 也不能有构造函数, 因为它被作为 Mixin 引用了
class MixinA{
String? name = 'MixinA';
// MixinA(); // 被作为混入的类,不能拥有构造函数
void printA(){
print('A');
}
void run(){
print('A is runing');
}
}
dart
//使用 mixin 声明的混入 MixinB
mixin MixinB{
String? name = 'MixinB';
void printB(){
print('B');
}
void run(){
print('B is runing');
}
}
泛型 (Generics)
泛型是在函数、类、接口中指定 宽泛数据类型 的语法
- 泛型函数
- 泛型类
- 泛型接口
通常, 在尖括号( <> )中, 使用一个字母来代表类型, 例如 E,T,S,K,和 V 等
作用: 使用泛型可以减少重复的代码
泛型函数
dart
// 泛型函数,既可以约束类型,又可以减少代码量
T getData<T>(T value){
return value;
}
// 只约定参数类型,不约定函数返回值的类型
getInfo<T>(T value){
return value;
}
void main(){
// 调用泛型函数
print(getData<int>(20)); // result:20
print(getData<String>('Hello')); // result:Hello
}
泛型类
dart
import 'GenericsClass.dart';
void main(){
// 实例化泛型类
GenericsClass g = GenericsClass<int>();
g.add(1);
g.add(9);
g.info(); // result:{1, 9}
// 实例化泛型类
GenericsClass gs = GenericsClass<String>();
gs.add('Hello');
gs.add('World');
gs.info(); // result:{Hello, World}
// 字面量形式泛型
Set s = <int>{}; // 只能在该Set里面添加 int 类型的成员
}
dart
// 泛型类
class GenericsClass<T>{
Set s = Set<T>();
void add(T value){
this.s.add(value);
}
void info(){
print(this.s);
}
}
泛型接口
文件结构
├─ lib
│ ├─ Cache.dart // 缓存,泛型接口
│ ├─ FileCache.dart // 文件缓存,实现泛型接口的类
│ └─ MemoryCache.dart // 内存缓存,实现泛型接口的类
└─ main.dart // 程序运行入口
dart
import './lib/FileCache.dart';
import './lib/MemoryCache.dart';
void main(){
// 文件缓存 - 缓存字符串
FileCache fc = FileCache<String>();
fc.setByKey('foo','bar'); // result:文件缓存: key=foo,value=bar
// 文件缓存 - 缓存Map数据
FileCache fm = FileCache<Map<String,String>>();
fm.setByKey('zoo',{'title':'标题党'}); // result: 文件缓存: key=zoo,value={title: 标题党}
}
dart
import './Cache.dart';
// 实现泛型接口的类
// FileChcha<T> 这里的 <T> 相当于传入 FileCache 后再传给 Cache
class FileCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print('文件缓存: key=${key},value=${value}');
}
}
dart
import './Cache.dart';
// 实现泛型接口的类
// MemoryCache 这里的 <T> 相当于传入 MemoryCache 后再传给 Cache
class MemoryCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print('内存缓存: key=${key},value=${value}');
}
}
dart
// 泛型接口
abstract class Cache<T>{
getByKey(String key);
void setByKey(String key,T value);
}
使用泛型限制参数类型
文件结构
├─ lib
│ ├─ Foo.dart // Foo类的泛型 <T> 继承了SomeBaseClass类,从而限制了泛型参数类型
│ ├─ Extender.dart // Extender 类继承了 SomeBaseClass 类
│ ├─ SomeBaseClass.dart // SomeBaseClass 类
│ └─ AnotherClass.dart // AnotherClass 类
└─ main.dart // 程序运行入口
dart
import './lib/Foo.dart';
import './lib/AnotherClass.dart';
import './lib/SomeBaseClass.dart';
void main(){
// 这里 Foo<> 泛型传递的参数符合 SomeBaseClass 类,代码运行正常
Foo someBaseClassFoo = Foo<SomeBaseClass>();
print(someBaseClassFoo); // result:Instance of 'Foo<SomeBaseClass>'
// 这个实例化是错误的,因为不符合 class Foo<T extends SomeBaseClass> 限定的要求
// Foo f = Foo<AnotherCalss>();
// 因为 Extender 继承了 SomeBaseClass ,代码运行正常
Foo extender = Foo<Extender>();
print(extender); // result:Instance of 'Foo<Extender>'
// 不传递泛型参数的时候,会默认传递 SomeBaseClass 泛型
Foo foo = Foo();
print(foo); // result:Instance of 'Foo<SomeBaseClass>'
}
dart
import './SomeBaseClass.dart';
class Foo<T extends SomeBaseClass>{
// 重写了 toString() 方法
@override
String toString() => "Instance of 'Foo<$T>'";
}
dart
import './SomeBaseClass.dart';
// Extender 继承了 SomeBaseClass
class Extender extends SomeBaseClass{
}
dart
class SomeBaseClass{
}
dart
class AnotherClass{
}
扩展 (extension on)
- extension 关键字在 Dart 2.7 及其以上才支持
sdk: ">=2.7.0"
- extension 可以扩展对象的内容
extension StringExtension on String{//扩展的内容};
- 扩展不仅可以定义方法, 还可以定义 setter,getter,operator
- 使用
- 声明扩展
- 引入扩展
- 使用扩展 (String.扩展内容)
dart
main() {
String number = '20';
print(number.parseInt()); // result: 20,parseInt() 不是 String 类内置的方法,只是因为写了扩展
Person p = Person();
p.say(); // result: Say something
print(p.name); // result: 学生
}
// 扩展内置类
extension StringExtension on String {
// 将字符串形式的数字,转成数字
parseInt() {
return int.parse(this);
}
}
// 自定义类
class Person {
say() {
print('Say something');
}
}
//扩展自定义类
extension studentExtension on Person {
// 扩展 Getter
get name => '学生';
}