1. 类与对象
在Flutter中,使用class 关键字声明一个类 ,使用new 加上构造函数 生成一个对象 ,所有的对象都默认继承Object类。Dart中会默认生成getter 和 setter方法,属性和方法都通过点语法 进行访问,Dart方法不支持重载。final修饰的属性必须定义初始值。
声明一个类:
class LGPerson {
String? name;
int? age;
final String hobby = 'coding';
void run() {
print("name:$name age:$age");
}
}
声明一个对象和方法调用:
LGPerson p = LGPerson();
p.name = 'LS';
p.age = 18;
p.run();
在Dart中,同文件是不存在私有化的,只有在不同文件中,才能有私有属性。 新建一个文件,并添加一个新的LGTeacher类,只对外暴露height属性和printTeacher方法。
class LGTeacher {
String? _name;
int? _age;
int? height;
void _run() {
print("name:$_name age:$_age");
}
void printTeacher() {
_run();
}
}
这里可以看到,外部就无法访问私有属性和方法了,而如果是同一个文件里面的class,则依然可以访问。
2. 构造函数
定义完一个类之后,这个类默认就有构造函数。相当于:
LGperson(){}
这里可以自定义构造函数,如果自定义了构造函数,那么默认的构造函数就无效了。
LGPerson(int age,String name){
_name = name;
_age = age;
}
LGPerson p = LGPerson(18,"test");
如果这里不是私有属性的话, 就会出现属性和参数名字一样的情况:
name = name;
age = age;
这里可以将参数改名字 或者在属性前面加this. ,但是推荐使用Dart语法糖来自动赋值。
LGPerson(this.age,this.name);
用了这个语法糖之后,属性也可以不需要变成Nullability的。
class LGPerson {
String name;
int age;
LGPerson(this.age,this.name);
void run() {
print("name:$name age:$age");
}
}
如果属性使用final修饰的,那么这个属性之后就不能改变了。 class 里面还可以创建命名构造函数:
LGPerson.withName(this.name,this.age);
// 调用
LGPerson p2 = LGPerson.withName("kk", 18);
final 有什么存在的意义呢?其实这里有个应用场景,就是常量对象。 当一个类的所有属性用final 修饰,构造方法用const 修饰,那么创造出来的对象就是常量对象了。(这里需要所有属性都用final修饰,否则构造方法用const修饰就会报错)。
class LGPerson {
final String name;
final int age;
final int height;
const LGPerson(this.age,this.name,this.height);
// LGPerson.withName(this.name,this.age);
void run() {
print("name:$name age:$age");
}
}
3. 工厂构造&单例对象&初始化列表
在Dart中创建单例,这样的话就是在方法里面调用自己,肯定是不行的。
class FactoryClass {
//需要一个单例对象
static FactoryClass? _instance;
FactoryClass(){
_instance ??= FactoryClass();
}
}
这时候就要用到命名构造函数。
class FactoryClass {
//需要一个单例对象
static FactoryClass? _instance;
FactoryClass(){
_instance ??= FactoryClass._init();
}
FactoryClass._init();
}
在dart中的构造函数是没有返回对象的,如果需要返回对象,则需要在方法前面加factory。这里因为_instance 的nullability 所以需要强制解包 。
class FactoryClass {
//需要一个单例对象
static FactoryClass? _instance;
factory FactoryClass(){
_instance ??= FactoryClass._init();
return _instance!;
}
FactoryClass._init();
}
工厂方法还可以优化一下
factory FactoryClass(){
return _instance ??= FactoryClass._init();
}
// 甚至
factory FactoryClass() => _instance ??= FactoryClass._init();
这样就成功创建了一个单例。 在Dart中,还有一个叫初始化列表的东西。初始化列表会在构造函数运行之前对属性进行赋值(需要注意的是,初始化变量的右侧不能访问this )。
初始化列表作用
class Person {
String name;
int age;
final height;
Person(this.name,this.age,int h):
height = h, assert(h>=0){
print('name: $name, age:$age,height:$height');
}
}
当传入的height小于0的时候就会报错。
4. 类方法&对象操作符
在Dart中,静态属性和静态方法(也就是类方法)使用static修饰。
class StaticClass {
//静态属性
static int count = 1;
// 静态方法
static int sum (int a ){
return a + count;
}
}
属性赋值和方法的调用需要用类而不是对象。
StaticClass.count = 10;
StaticClass.sum(20);
要注意的是类方法里面是无法访问非静态属性的,但是实例方法里面是可以访问静态属性的。这是因为静态方法是通过类对象来调用的,在类对象调用之前,实例对象有可能是不存在的,那么实例属性和方法就不存在所以无法访问。但是实例方法通过实例对象访问,实例对象创造出来之后类对象是一定加载进去了的,所以就可以访问静态属性和方法。 常量属性必须用static修饰否则会报错。
static const int age = 10;
在使用对象的时候,有可能出现对象为空的情况,这时候如果调用方法,那么就会报错。
var s1;
s1= StaticClass();
s1 = null;
s1.sum2();
为了安全起见,可以在对象后面加上? 后调用方法,也就是:
s1?.sum2()
这个时候如果对象为空,那么就会得到null。
这个时候s1的类型就会变为Object,调用StaticClass 类中的方法就会报错。
这时候需要进行类型强转。
或者进行类型判断。
void staticDemo() {
var s1 = Object();
s1= StaticClass();
if (s1 is StaticClass) {
s1.total();
}
}
5. Dart 的继承
在Dart中类是单继承的,使用extends 来继承一个类,子类会继承除了构造方法 之外的属性和方法。
创建一个Person类。
class Person {
String? name;
int? age;
int? _height;
bool get isFree => _height!< 110;
run() {
print("run run run");
}
创建一个Student类继承自Person。
class Student extends Person {
study() {
print("study study study");
}
}
子类可以访问父类的非私有属性和方法,
Student s1 = Student();
s1.age = 20;
s1.run();
子类可以重写父类的方法,这样子类就会调用重写后的方法。
@override
// TODO: implement isFree
bool get isFree => age! < 18;
@override
run() {
// TODO: implement run
print("student run");
}
子类会自动继承默认构造方法。如果是有名字或者带参数的构造方法的话,那么就要主动调用。 父类
class Person {
String? name;
int? age;
int? _height;
bool get isFree => _height!< 110;
run() {
print("run run run");
}
Person(this.age){ print("Person Person Person");}
Person.init();
}
子类(这里可以只实现一个):
class Student extends Person {
Student.init() : super.init();
Student(int? age) : super(age);
study() {
print("study study study");
}
@override
run() {
// TODO: implement run
print("student run");
}
@override
// TODO: implement isFree
bool get isFree => age! < 18;
}
6. 抽象类和接口
抽象类是不能被实例化的类,使用abstract修饰。 抽象类中可以声明抽象方法。
abstract class AbstractClass {
// 抽象方法,只能放在抽象类
int sum(int a,int b);
其他的类如果继承自抽象类,则必须实现抽象类里面的所有抽象方法。
class Subclass extends AbstractClass {
@override
int sum(int a, int b) {
return a + b;
}
}
这里就有个问题,这样就只能继承一个抽象类。这里就需要用implements来实现继承多个抽象类。
class Subclass implements AbstractClass,AbstractClass1 {
@override
int sum(int a, int b) {
return a + b;
}
}
如果implements 一个class,则需要实现所有的属性和方法。当然这样是 没必要的,一般只对抽象类进行implements。
7. 混入Mixins
mixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法、变量而不必成为其子类。Mixin 的作用就是在多个类层次结构中重用类的代码。 Mixins 其实就是多继承:
class D extends A with B,C {
}
这样就可以调用不同类中的方法。
D d = D();
d.a();
d.b();
d.c();
这里有个问题,如果三个类中的方法是同一名字,那么会调用哪个类的呢?运行后发现是c,那么也就是说,这里会调用最后一个继承的类里面的方法。 混入有个限制就是混入的类不能实现构造函数,否则就会报错。 作为混入的类如果有继承除Object以外的类的话,那么就会提醒The class 'C' can't be used as a mixin because it extends a class other than 'Object'. 。这是因为如果C继承的类如果有带有参数的构造函数,那么就会报错。 如果D中没有自己的属性和方法的时候,那么混入就可以缩写成。
class D = A with B,C;
8. 操作符重载
在Dart中,如果想要比较两个类的大小或者其他的操作,需要用到操作符重载。 如果没有操作符重载,下面的代码是会报错的。
void operatorDemo() {
OperatorClass o1 = OperatorClass(22);
OperatorClass o2 = OperatorClass(23);
print(o1 > o2);
}
重载了>号之后,就可以正常的比较大小了。
class OperatorClass {
int age;
OperatorClass(this.age);
bool operator >(OperatorClass other) => this.age > other.age;
}
|