class类
概述:类是对一类具有相同特征事物的描述,是实例的构造模板,是一个抽象概念 。例如,造汽车,首先我们得有制造图纸,图纸上绘汽车的底盘,轮胎,发动机等等,而我们根据图纸制造的出来的车就是这个图纸的一个实例(即类的实例化)。根据图纸我们我可制造很多相同的汽车出来,为了区分它们,我们会给每辆汽车一个编号,对应到代码层面,也就是一个类可以构造出多个数据结构相同的实例对象,而我们通过不同的引用来找到它们,实例化的时候系统会给每个实例在内存分配一个地址值来区分们,引用指向它们的地址值。
dart是一门面向对象语言,并且基于mixin-based多继承,每个对象都是一个类的实例。
1、类的声明: class 类名 {}
以下划线或者大写字母开头,可以包含数字$(当然如果你小写字母开头并不会报错,但是约定俗成的都是以大写字母或下划线开头)
class Person {}
class _Person {}
注意:如果以下划线开头,表示这个类是私有的,只能在当前文件使用。
2、类的组成:方法,属性,构造器
//一个简单类的声明
class Test {
String name;
Test(this.name);
say() {}
}
①类的方法,分为静态方法和实例方法,静态方法用static关键字修饰,是属于类本身的
静态方法调用:类名.方法名()
实例方法调用:实例.方法名()
void main() {
//类的方法调用
Person.say();
//实例方法调用
Person p = new Person();
p.eat();
}
class Person {
static say() {
print("静态方法");
}
eat() {
print("实例方法");
}
}
②类的属性,分为静态属性和实例属性
静态属性使用static const 声明,并且在声明类的时候必须给其赋值
void main() {
print(Person.age);
}
class Person {
static const age = 23;
}
实例属性分为常量属性和变量属性,常量属性使用final关键字修饰,必须在实例化或者类声明的时候赋值,常量属性赋值后不能被修改。变量属性可以在实例化对象后通过对象来赋值
void main() {
Person p = new Person("sixleg");
p.age = 23;
print(p.nation); //China
print(p.name); //sixleg
print(p.age); //23
}
class Person {
final String nation = "China"; //常量属性,类声明时赋值
final String name; //常量属性,实例化时赋值
int age; //变量属性
Person(this.name);
}
建议使用"?."代替"."来给实例属性赋值,这样可以避免当引用的值为null时程序报错
var p;
p.age = 23; //报错
p?.age = 23; //不会报错,如果p不为null,则赋值
③类的构造器,又叫构造方法
在dart中构造器不能被子类继承,构造器通过ClassName或者ClassName.identifier来命名,如果不显示声明构造器,dart会给每个类隐式添加一个无参名字跟类名相同的构造器,如果显示声明了构造器,则这个构造器不会存在,一个类可以有多个构造器。
class Person {
String name;
int age;
Person() {} //无参构造器
Person.fillName(String name) { //位置参数
this.name = name; //this关键字指向当前的实例对象
}
Person.fiiAge({int age}) { //命名参数
this.age = age;
}
}
//构造器可以简写,下面这种写法跟上面效果一样
//class Person {
// String name;
// int age;
// Person();
// Person.fillName(this.name);
// Person.fiiAge({int this.age});
//}
构造器的调用,类实例化对象时调用对用的构造器,通过new关键字或者像调用函数那样调用
void main() {
var p1 = new Person();
var p2 = Person.fillName("六只脚");
var p3 = Person.fiiAge(age: 23);
}
常量构造器,当你构造的对象不会被改变时,可以使用常量构造器,在构造器前面加上const关键字修饰,当一个类声明了一个常量构造器,则不能存在非常量构造器,并且这个类中的实例属性必须声明为常量属性,且必须在声明或者实例化时被初始化。当使用const调用常量构造器时,如果参数相同,则为同一实例对象。
void main() {
var p = const Person(23);
var p2 = const Person(23);
var p3 = const Person(27);
var p4 = Person(23);
var p5 = Person(23);
print(p == p2); //true
print(p == p3); //false
print(p == p4); //false
print(p4 == p5); //false
}
class Person {
final int age;
const Person(this.age);
static Person person = const Person(18);
}
通过runtimeType属性来获取实例对象的类型
var p = Person();
print(p.runtimeType); //Person
重定向构造器,重新定向到该类的另一个构造器
class Dog {
String name;
int age;
Dog(this.name, this.age);
//重定向到该类的主构造器,主构造器指与类名相同的构造器,当然,也可以定向到其它构造器
Dog.fillName(String name) : this(name, 23);
}
工厂构造器,通过factory关键字来声明,如果创建的对象相同,则不需要重新创建,直接从缓存的返回对象(工厂构造器里不能使用this关键字)
main() {
Dog d = Dog("six");
Dog d1 = Dog("six");
print(d == d1); //true
d.name = "leg";
print(d1.name); //leg
}
class Dog {
String name;
static final Map<String, Dog> _cache = <String, Dog>{};
factory Dog(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final dog = Dog.getDog(name);
_cache[name] = dog;
return dog;
}
}
Dog.getDog(this.name);
}
④抽象类
使用关键字abstract来修的类称为抽象类,抽象类可以有静态方法,静态属性跟实例方法和实例属性,抽象类的实例方法的方法体可以省略,至于为什么什么后面接口的实现会提到。一般抽象类用来定义接口,抽象类不能被实例化,如果想实例化抽象类,可以使用factory工厂构造器。
abstract class Person {
int age;
final String name;
Person(this.name);
void say();
static eat() {}
}
⑤接口的实现
dart的每个类都隐式定义了一个接口(不管是一般类还是抽象类),接口中包含该类的所有实例成员(构造函数不包括),如果你想不在继承的情况下一个类A支持另一个类B的API,则类A可以使用关键字implements来实现B的接口,一个类可以实现一个或多个类的接口。
abstract class Person {
int age;
final String name;
Person(this.name);
void say();
static eat() {}
}
class Man implements Person {
get age => this.age;
set age(int age) => this.age = age;
void say() {}
get name => this.name;
}
注意:当A类实现B类的接口时,A类中必须重写B类中所有实例属性的getter和setter方法,如果属性是final修饰的常量,则只需重写getter方法,实例方法也要被重写。这也是前面提到抽象类的方法体被可以省略的原因,因为抽象类一般是为了定义接口,接口中的方法要被实现的类重写,所以在接口中的实例方法体体也就没有意义。
⑥类的继承
如果你想创建一个子类,请使用关键字extends,子类中会隐式添加一个super属性,super代表父类。继承是单继承的,这与接口实现不同,一个子类只能有一个父类。
子类不能继承父类的构造器,子类的构造器会默认调用父类的无参主构造器(与类同名的构造器),如果子类想调用父类的其它构造器则需手动调用,如果父类不存在这个构造器,则不能调用,子类也不能声明带此参数的构造器。
void main() {
Man.fill(); //默认调用父类的Person()构造器
}
class Person {
int age;
Person();
Person.fill(this.age) {
print("super fill");
}
}
class Man extends Person {
Man();
Man.fill();
}
使用冒号:来调用父类构造器,且子类构造器不能简写,可以省略花括号。
class Animal {
int age;
Animal();
Animal.withAge(this.age);
}
class Cat extends Animal {
Cat(int age) : super.withAge(age);
}
方法的重写,子类可以重写父类的方法,通过super.methodName()调用父类的方法,dart中方法不能重载,这与java不同
class Animal {
say() {}
}
class Cat extends Animal {
@override
say() {
super.say();
}
}
⑦mixin,(混入的意思)给一个类添加特征,使用关键字with
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
为了使用mixin,我们必须创建一个继承Object类的类,并且不能声明构造器,使用mixin关键字代替class关键字
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
可以通过关键字on指定mixin只允许哪个类使用
mixin MusicalPerformer on Musician { //只有Musician能使用
// ···
}