Dart语言之面向对象
目录
前言
1、初始化列表和标准构造方法
2、命名构造方法
3、工厂构造方法
4、命名工厂构造方法
5、set&get&静态方法
6、抽象类
7、mixins
8、方法
9、泛型
前言
本篇继续来学习Dart语言的内容,今天接上一篇《Dart语言之常用数据类型》来介绍Dart里面的面向对象。面向对象的核心特性包括封装、继承和多态。
1、初始化列表和标准构造方法
类是面向对象编程的核心概念,包含对象、变量和方法。在Dart中定义类时,所有类默认继承自Object。
定义类变量的语法为:在类的大括号内直接声明变量类型和变量名,构造方法用于初始化类的对象。多态的表现形式包括方法重载,例如重写父类的toString方法,举个栗子如下:
/// 定义一个类,所有的类都继承自Object
class Person {String name;int age;/// 标准的构造方法Person(this.name, this.age);/// 重写父类方法@overrideString toString() {return '$name,$age';}
}
初始化列表位于构造方法冒号后的表达式,用于初始化类的变量。
私有变量通过下划线标识,仅在当前文件内可访问:String _school;
可选参数通过大括号声明:Student({String city});
默认参数在可选参数后通过等号设置默认值,例如:Student({String country = 'China'});
可选参数和默认参数不仅适用于构造方法,也适用于其他类型的方法。
初始化列表可在子类构造方法体之前设置,用于初始化实例变量。若父类没有无参构造方法,则需在初始化列表中调用父类的构造方法。构造方法的方法体可通过大括号设置,但非必须。示例:
class Student extends Person {// 定义类的变量String? _school; // 私有变量通过下划线标识,仅在当前文件内可访问String? city;String? country;late String name;/// 构造方法:/// 通过this._school初始化自有参数/// name,age交给父类进行初始化/// city为可选参数/// country为默认参数Student(this._school,String name,int age, {this.city,this.country = '中国',}) : // 初始化列表可在子类构造方法体之前设置,用于初始化实例变量。例如:name = '$country-$city',// 若父类没有无参构造方法,则需要在初始化列表中调用父类的构造方法super(name, age) {// 构造方法体不是必须的print('构造方法的方法体不是必须的');}
}
2、命名构造方法
命名构造方法是开发过程中常见的一种构造方法形式。命名构造方法以类名开头,通过点符号连接自定义方法名,用于实现类的多构造方法功能。
// 命名构造方法【类名+.+方法名】
Student.convert(Student stu) : super(stu.name, stu.age);
3、工厂构造方法
在 Dart 中,使用 factory 关键字在构造方法前定义一个工厂构造方法。它允许你在构造函数中执行自定义逻辑,比如返回一个已存在的实例(实现单例模式)、返回子类实例,或者根据条件返回不同类型的实例。与普通构造函数不同,工厂构造方法需要显式使用 return 关键字来返回一个对象,并且不一定要返回新的实例。
定义工厂构造方法:
- 使用
factory关键字:在类定义中,在构造方法的声明前加上factory关键字。 - 实现逻辑:在方法体内部编写创建和返回对象的逻辑。
- 使用
return关键字:工厂构造方法必须使用return关键字返回一个对象。
class Singleton {static Singleton? _instance; // 静态变量,缓存实例// 私有命名构造函数Singleton._internal();// 工厂构造方法factory Singleton() {if (_instance == null) {// 如果实例不存在,就创建一个新的实例_instance = Singleton._internal();}// 返回缓存的实例return _instance!;}
}void main(){var s1 = Singleton();var s2 = Singleton();print(s1 == s2); // 输出:true
}
4、命名工厂构造方法
命名工厂构造方法的主要形式如下:
- 通过factory关键字标识
- 类名加点加自定义方法名构成
- 参数可根据需要设置
- 可返回构造方法并根据需要进行初始化
- 无需将类final变量作为参数,是一种灵活的获取类对象的方式
// 命名工厂构造方法:factory [类名+.+方法名]// 它可以有返回值,而且不需要将类的final变量作为参数,是一种灵活获取类对象的方式factory Student.stu(Student stu){return Student(stu._school, stu.name, stu.age);}
5、set&get&静态方法
set方法用于修改私有字段值,需使用set关键字声明:
// 可以为私有字段设置setter来控制外界对私有字段的修改set school(String value) {_school = value;}
get方法用于为私有字段设置访问接口,允许外部代码获取私有字段值:
// 可以为私有字段设置getter来让外界获取到私有字段String? get school => _school;
静态方法使用static关键字标识,属于类而非实例,调用方式为类名.方法名(),无需创建类实例:
// 静态方法static doXXX(String str) {print('doXXX:$str');}
6、抽象类
抽象类的定义需使用abstract关键字修饰。例如,定义Study抽象类时需标注为abstract class Study。抽象类的作用主要体现在接口定义场景。
/// 使用abstract来定义抽象类,该类不能被实例化,在定义接口时非常有用
abstract class Study{void study(); // 抽象方法@overrideString toString() {return '抽象类中的普通方法';}
}
/// 继承抽象类要实现它的抽象方法,否则也需要将自身定义成抽象类
class StudySon extends Study{@overridevoid study() {// TODO: implement study}
}
7、mixins
mixin的特征包括:
- 必须创建继承自object的子类
- 不能继承其他类
- 不能声明构造方法
- 不能调用super
mixin class OldMan{void workOld(){print('OldMan');}
}
mixin class YoungMan{void workYoung(){print('YoungMan');}
}
class Worker with OldMan,YoungMan{@overridevoid workOld() {// TODO: implement workOldsuper.workOld();}@overridevoid workYoung() {// TODO: implement workYoungsuper.workYoung();}
}class Worker extends OldMan with YoungMan{@overridevoid workOld() {// TODO: implement workOldsuper.workOld();}@overridevoid workYoung() {// TODO: implement workYoungsuper.workYoung();}
}
8、方法
①、方法的完整结构包含以下要素:
- 返回值:定义方法执行后的输出类型,可为void或具体数据类型
- 参数:接收外部输入的变量集合
- 方法名:方法的唯一标识符(匿名方法除外)
其中,返回值特性包括:
- 类型可缺省,默认返回void类型
- 需显式声明返回具体数据类型时,必须包含return语句
- 匿名方法作为特例可不声明方法名
以及,方法参数具有以下特征:
- 参数类型与参数名共同构成参数声明
- 参数分类:
- 可选参数
- 默认参数
- 构造方法的参数规则完全适用于普通方法
②、私有方法的核心特征:
- 命名规范:以下划线开头
- 作用域限制:仅在定义文件中可访问
应用范围:同样适用于私有字段和私有类
③、匿名方法的定义:
- 无方法名的即时函数定义
常见应用场景:作为回调函数参数
class FunctionLearn {int sum(int value1, int value2) {return value1 + value2;}_learn() {print('私有方法');}anonymousFunction() {const list = ['func1', 'func2'];list.forEach((item) {print('item: $item');});}
}
关于其它类型的方法在上面基本上都已经介绍了,这里不再赘述。
9、泛型
最后来看一下泛型的使用,泛型的核心价值在于提升代码复用性,支持对非特定数据类型的通用操作。在 Dart 中,通过在类名后添加一对尖括号 <T> 来定义泛型类,其中 T 是一个泛型占位符,表示一个不确定的类型。如果需要多个泛型参数,可以使用逗号分隔,例如 <T, E>。泛型类可以在实例化时指定具体的类型,以提高代码的复用性和类型安全。
示例代码如下:
class Cache<T> {final Map<String, T> _cached = <String, T>{};void setItem(String key, T value) {_cached[key] = value;}T? getItem(String key) {return _cached[key];}
}void main() {var ca = Cache<String>();ca.setItem('key', '123');print(ca.getItem('key'));
}
关键点:
- 定义泛型类:在类名后面使用尖括号
<T>来声明泛型参数。 - 使用泛型参数:在类的方法、属性等中使用泛型参数
T来表示一个不确定的类型。 - 实例化:在创建类的实例时,通过
<具体类型>指定泛型参数的类型,例如Cache<int>()。 - 多个泛型参数:可以使用逗号分隔多个泛型参数,例如
class MyMap<K, V>。 - 类型约束:可以使用
extends关键字来限定泛型参数的类型,例如class NumericCache<T extends num>表示T必须是num或其子类。
OK,到这里今天的内容就介绍完了,更多详情请参考文档:https://dart.dev/docs
下期再会,祝:工作顺利!
