Dart2 类与对象
类和对象
class 关键字声明一个类
创建对象 使用 new + 构造函数()
所有的对象都是继承
Object
,属基类
创建一个类
void main() {
Rcperson p1 = Rcperson();
p1.name = 'roc';
p1.age = 8888;
p1.run();
var p2 = Rcperson();
p2.name = 'cor';
p2.age = 6666;
p2.run();
}
class Rcperson {
String? name;
num? age;
void run(){
print('name:$name age:$age');
}
}
name:roc age:8888
name:cor age:6666
dart中同一个作用域内不能有两个同名方法,方法名称是寻找方法实现的唯一标识
创建一个 Dart file 内部定义一个Person类
class Person {
String? name;
num? age;
void run(){
print('name:$name age:$age');
}
}
- 在main.dart中 导入 的新创建的dart file 的头部信息,就可以使用了
// 导入头部信息
import 'roc_test_demo.dart';
Person p3 = Person();
p3.name = '大牛';
p3.age = 2222;
p3.run();
// 打印 name:大牛 age:2222
- _ 下划线内部成员的构建(私有化),外部不能直接访问,同一个文件中可以访问
_ 下划线成员变量的作用域是当前文件的内部的,外部文件不能访问,即文件私有化
class Student{
String? _name;
num? _age;
void _run(){
_name = '飞机';
_age = 18;
print('name:$_name age:$_age');
}
void getContent(){
_run();
}
}
Student s1 = Student();
s1.getContent();
name:飞机 age:18
- 自定义类的构造函数(一般使用默认的构造函数)
- 一旦自定义类构造函数,默认的构造函数就不起作用了
class Arsenal1{
String? _name;
num? _price;
// 添加构造函数
Arsenal1(String name,int price){
_name = name;
_price = price;
}
void _run(){
print('name:$_name price:$_price');
}
void getContent(){
_run();
}
}
class Arsenal1{
String? _name;
num? _price;
// 添加构造函数 推荐使用
Arsenal1(this._name,this._price);
void _run(){
print('name:$_name price:$_price');
}
void getContent(){
_run();
}
}
Arsenal1 a1 = Arsenal1('导弹', 40000);
a1.getContent();
name:导弹 price:40000
- 自定义命名构造函数
class Arsenal1{
String? _name;
num? _price;
// 自定义命名构造函数
Arsenal1.withName(this._name,this._price);
void _run(){
print('name:$_name price:$_price');
}
void getContent(){
_run();
}
}
Arsenal1 a2 = Arsenal1.withName('巡逻舰', 100000);
a2.getContent();
name:巡逻舰 price:100000
- 常量对象
当对象的成员属性都是final修饰,这个对象可以被创建为常量对象,使用const修饰构造函数
// 常量对象
class Rcperson2{
final String name;
final num age;
const Rcperson2.withName(this.name,this.age);
void run(){
print('name:$name age:$age');
}
}
// 常量对象 初始化之后不能做任何的修改
Rcperson2 constP = Rcperson2.withName('CJL', 2);
constP.run();
工厂构造 & 单例对象
-
工厂构造
- 创建一个 dart file ,命名为 factory_test_demo.dart
- 构造函数是不能添加return的,factory修饰的工厂方法除外
单例对象
构建单例对象的
方法名
必须与当前对象的类名一样
,不能和OC或swift一样写个sharedInstance.方式一
class Factory1{
static Factory1? _instance;
// 创建私有的构造函数,将默认的构造函数私有化
Factory1._init();
// 单例的方法名同类名
factory Factory1(){
if (_instance == null){
_instance = Factory1._init();
}
return _instance!;
}
}
// 单例对象
Factory1 f1 = Factory1();
Factory1 f2 = Factory1();
print(f1 == f2); // true
- 方式二
class Factory2{
static Factory2? _instance;
// 创建私有的构造函数,将默认的构造函数私有化
Factory2._init();
// 单例的方法名同类名
factory Factory2(){
return _instance ??= Factory2._init();
}
}
// 单例对象
Factory1 f1 = Factory1();
Factory1 f2 = Factory1();
print(f1 == f2); // true
- 方式三 推荐使用
class Factory3{
static Factory3? _instance;
// 创建私有的构造函数,将默认的构造函数私有化
Factory3._init();
// 单例的方法名同类名
factory Factory3() => _instance ??= Factory3._init();
}
// 单例对象
Factory1 f1 = Factory1();
Factory1 f2 = Factory1();
print(f1 == f2); // true
- 初始化列表 冒号后面是初始化列表
方式一
class InitializetionList{
String name;
num age;
final _h;
// 创建初始化列表
InitializetionList(this.name,this.age,num h):_h = h,assert(h>170);
}
// 初始化对象
InitializetionList ill = InitializetionList('roc', 18, 180);
方式二 推荐使用 先校验 再赋值
class InitializetionList2{
String name;
num age;
final _h;
// 创建初始化列表
InitializetionList2(this.name,this.age,num h):
_h = h,
assert(h>170),
assert(age>=18){
print('name:$name age:$age height:$_h');
}
}
// 初始化对象
InitializetionList2 ill2 = InitializetionList2('roc', 18, 180);
// name:roc age:18 height:180
方式三 推荐使用 先校验 再赋值
class InitializetionList3{
String _name;
num _age;
num _h;
InitializetionList3(String name,num age,num height):
_name = name,
_age = age,
_h = height,
assert(name != null && age >= 18 && height >= 170) {
print('name:$_name age:$_age height:$_h');
}
}
InitializetionList3 ill3 = InitializetionList3('roc', 18, 190);
// name:roc age:18 height:190
类方法 & 对象操作符
类方法 static
- 静态方法(类方法)不能访问非静态成员
- 实例方法 即可访问静态成员,亦可访问非静态成员
- 理解访问原理:
- 实例未被创建的时候哪来的实力对象,这就是类方法中不能访问实例对象的原因
- 实例创建的时候类的信息也被加载到内存中,这也是实例能够访问静态成员的原因
- 究其根本:是dart语言作者对整个访问流程的控制
class StaticClass{
// 静态属性
static num count = 0;
// 静态方法
static sum1(a,b){
return a + b;
}
// 实例方法 访问静态变量
num sum2(a,b) => a + count + b;
}
// 类方法 和 类属性
num count = StaticClass.sum1(20,50);
StaticClass.count = count;
print(StaticClass.count); // 70
// 实例方法
StaticClass st = StaticClass();
num totalCount = st.sum2(60, 70);
print(totalCount); // 200
- 类中定义的常量用static修饰,否则报错
class Rcperson1 {
String? name;
num? age;
// 类中的常量用static修饰
static const String str = 'hi';
void run(){
print('name:$name age:$age');
}
}
- dynamic 类型可以为null,可用‘?’修饰对象,否则对象不存在时会报错,容错处理的一种手段
- 对于dynamic在访问成员的时候加上‘?’ 如:st2?.sum(60,70);
- 具体类型在声明时就可加上'?' 如:String? ct4;
案例1
var st2;
st2 = StaticClass();
num ct1 = st2.sum2(60,70);
print(ct1);
st2 = null;
var ct2 = st2?.sum(60,70);
print(ct2); // null
var ct3 = st2.sum(60,70);
print(ct3); // 报错
案例2
String? ct4;// 有 '?'
ct4 = 'roc';
ct4 = null;
print(ct4); // null
String ct5; // 没 '?'
ct5 = 'roc';
ct5 = null;
print(ct5); // 报错 非可选类型不能赋值为null
对象操作符 as is ..
-
强制类型转换符 as
- StaticClass zz = cc as StaticClass
方式一
var cc;
cc = Object();
cc = StaticClass(); // 准备调用sum2(),没有发现sum2()
// 强转 调用
StaticClass zz = cc as StaticClass;
num zzNum = zz.sum2(60, 70);
print(zzNum); // 200
// 强转之后 cc的类型
print(zz.runtimeType);// StaticClass
方式二
var cc;
cc = Object();
cc = StaticClass(); // 准备调用sum2(),没有发现sum2()
// 强转 调用
num ccNum = (cc as StaticClass).sum2(60, 70);
print(ccNum); // 200
// 强转之后 cc的类型
print(cc.runtimeType);// StaticClass
- 类型判断符 is
// 类型判断(自省)
bool hasStaticClass = zz is StaticClass;
print(hasStaticClass); // true
链式编程符 ..
对象使用'..'符号操作访问成员之后会返回对象本身,这就是链式编程产生的原理
// 链式编程符 每次调用返回对象本身
var customObject = zz..sum2(60, 70)..sum2(80, 90)..sum2(100, 110);
print(customObject.runtimeType); // StaticClass
继承 extends
dart是单继承
子类会继承所有的属性和方法(
构造方法除外
)
// 继承
class K {
String? name;
num? age;
final _height = 111;
bool get isFree => _height < 110;
run(){
debugPrint('K Running..');
}
}
class KK extends K{
@override
// TODO: implement isFree
bool get isFree => age! < 18;
void study(){
debugPrint('KK 在认真学习..');
}
}
- 多态
// 多态
void extendsDemo2(){
K k = KK();
k.run();
if (k is KK) {
k.name = 'roc';
k.age = 17;
k.study();
k.run();
}
bool hasFree = k.isFree;
debugPrint('是否免费:$hasFree');
-
重写toString
- 相当于当前类的描述信息
@override
String toString() {
// TODO: implement toString
String type = this.runtimeType.toString();
return '$type Type';
}
// KK Type
- 子类会默认继承父类的默认构造方法
- 父类若定义类自己的构造方法,继承的子类必须重写,否则报错
方式一
class KPerson{
String? name;
KPerson.withName(this.name);
KPerson.init();
KPerson(this.name);
}
class KStudent extends KPerson{
String? name;
// 子类中至少重写一个父类的构造方法
KStudent(this.name):super(name);
KStudent.init():super.init();
KStudent.withName(this.name):super.withName(name);
}
方式二
class KPerson{
String? name;
KPerson.init();
KPerson.withName(this.name);
KPerson(this.name);
}
class KStudent extends KPerson{
final String? subName;
// 子类中至少重写一个父类的构造方法
KStudent.init():subName = 'roc',super.init();
KStudent.withName(String? hasName): subName = hasName ,super.withName(hasName);
KStudent(String? hasName): subName = hasName,super(hasName);
}
提示 初始化列表放在super前面
抽象类 & 接口
抽象类 单继承
- 抽象类不能被实例化,使用
abstract
修饰
// 提示 方法最好使用驼峰命名,否则编译器会警告
// 定义一个抽象类
abstract class KAbstractClass{
// 定义一个抽象方法 类似于ios中的protocol
num kSum(a,b);
}
class KAbstractSubClass extends KAbstractClass{
@override
num kSum(a, b) {
// TODO: implement kSum
return a + b;
}
}
void abstractDemo(){
// 不需要is判断,可直接使用子类成员
KAbstractClass as = KAbstractSubClass();
num count = as.kSum(60, 70);
debugPrint(count.toString());
}
// 结果130
- 注意是
debugPrint
不会报警告一切打印由字符串信息输出,打包时不用去除(推荐使用)
接口 implements 多实现
// 定义三个抽象类
abstract class KAbstractClass1{
// 定义一个抽象方法 类似于ios中的protocol
num kSum1(a,b);
}
abstract class KAbstractClass2{
// 定义一个抽象方法 类似于ios中的protocol
num kSum2(a,b);
}
abstract class KAbstractClass3{
// 定义一个抽象方法 类似于ios中的protocol
num kSum3(a,b);
}
class MAbstractSubClass implements KAbstractClass1,KAbstractClass2,KAbstractClass3{
@override
num kSum1(a, b) {
// TODO: implement kSum1
return a ~/ b;
}
@override
num kSum2(a, b) {
// TODO: implement kSum2
return a * b;
}
@override
num kSum3(a, b) {
// TODO: implement kSum3
return a - b;
}
}
void implementsDemo(){
MAbstractSubClass hasObject = MAbstractSubClass();
num c1 = hasObject.kSum1(20000, 1);
num c2 = hasObject.kSum2(10000, 2);
num c3 = hasObject.kSum3(30000, 10000);
debugPrint(c1.toString()); // 20000
debugPrint(c2.toString()); // 20000
debugPrint(c3.toString()); // 20000
}
Mixins 混入 多继承
- 1.有构造方法的类,不能用来混入
- 2.混入的类不能继承自Object之外的其他类,要保持单纯
方式一
class A {
message()=> debugPrint('A...');
send1()=> debugPrint('s1...');
}
class B {
message()=> debugPrint('B...');
send2()=> debugPrint('s2...');
}
class D {
message()=> debugPrint('D...');
send3()=> debugPrint('s3...');
}
// C继承自A,BD则是混入的类
class C extends A with B,D{
centerMessage()=> debugPrint('CM...');
}
void MixinClassDemo1(){
C hasObject = C();
hasObject.send1();
hasObject.send2();
hasObject.send3();
hasObject.message();
hasObject.centerMessage();
}
// 打印
s1...
s2...
s3...
D... 重点细节:message 打印的是最后一个混入类D的成员
CM...
方式二
class A {
message()=> debugPrint('A...');
send1()=> debugPrint('s1...');
}
class B {
message()=> debugPrint('B...');
send2()=> debugPrint('s2...');
}
class D {
message()=> debugPrint('D...');
send3()=> debugPrint('s3...');
}
class F {
message()=> debugPrint('F...');
}
// 推荐使用
class Fs = A with B,D,F;
void MixinClassDemo2() {
Fs hasObject = Fs();
hasObject.send1();
hasObject.send2();
hasObject.send3();
hasObject.message();
}
// 打印
s1...
s2...
s3...
F...
- 重载操作符 operator
class KOperatorClass{
num age;
KOperatorClass(this.age);
// 重载操作符 > 大于号
bool operator > (KOperatorClass otherObject) => age > otherObject.age;
}
// 使用
void kOperatorDemo(){
KOperatorClass k1 = KOperatorClass(36);
KOperatorClass k2 = KOperatorClass(18);
bool has = k1 > k2;
debugPrint(has.toString()); // true
}