1.类的定义
- Dart中定义类用class关键字 类通常有两部分组成:成员(member)和方法(method)
代码示例:
class 类名 {
类型 成员名;
返回值类型 方法名(参数列表) {
方法体
}
}
// 在方法中使用属性(成员/实例变量)时,并没有加this
// Dart在方法中通常使用属性时,会省略this,但是有命名冲突时,this不能省略
class Person {
String name;
eat() {
print('$name在吃晚饭');
}
}
main(List<String> args) {
// 创建类的对象 从Dart2开始,new关键字可以省略
var p1 = new Person();
// 给对象的属性赋值
p1.name = 'wang';
// 使用 ?. 来代替 . , 可以避免因为左边对象可能为 null,导致的异常
p1?.name = 'wang';
// 调用对象的方法
p1.eat(); // wang在吃晚饭
}
2.类的构造方法
2.1 普通构造方法
- 当通过类创建一个对象时,会调用这个类的构造方法, 当类中没有明确指定构造方法时,将默认拥有一个无参的构造方法,默认构造方法没有参数并会调用父类的无参构造方法。我们也可以根据自己的需求,定义自己的构造方法 当有了自己的构造方法时,默认的构造方法将会失效,不能使用
代码示例:
class Person1 {
String name;
int age;
// Person1(String name, int age) {
// this.name = name;
// this.age = age;
// }
// 在实现构造方法时,通常做的事情就是通过参数给属性赋值
// 为了简化这一过程, Dart提供了一种更加简洁的语法糖形式.
// 上面的构造方法等同于
Person1(this.name, this.age);
@override
String toString() {
return 'name = $name age = $age';
}
}
var p2 = Person1('wang', 18);
print(p2); // name = wang age = 18
2.2. 命名构造方法
- Dart不支持方法(函数)的重载,所以我们没办法创建相同名称的构造方法。我们需要使用命名构造方法。
ps:子类不会继承父类的构造方法。 子类不声明构造方法,那么它就只有默认构造方法
代码示例:
class Person2 {
String name;
int age;
Person2() {
name = '';
age = 0;
}
// 命名构造方法
Person2.withArgements(String name, int age) {
this.name = name;
this.age = age;
}
@override
String toString() {
return 'name = $name age = $age';
}
}
var p3 = Person2();
print(p3); // name = age = 0
var p4 = Person2.withArgements('wang', 18);
print(p4); // name = wang age = 18
2.3. 初始化列表
代码示例:
class Point {
final num x;
final num y;
final num distance;
// 错误写法
// distance = sqrt(x * x + y * y);
// }
// 正确的写法
Point(this.x, this.y) : distance = sqrt(x * x + y * y);
@override
String toString() {
return 'x = $x y = $y distance = $distance';
}
}
var point = Point(10, 20);
print(point); // x = 10 y = 20 distance = 22.360679774997898
- 上面这种初始化变量的方法, 我们称之为初始化列表(Initializer list)
2.4. 重定向构造方法
- 在某些情况下, 我们希望在一个构造方法中去调用另外一个构造方法, 这个时候可以使用重定向构造方法
代码示例:
class Person3 {
String name;
int age;
Person3(this.name, this.age);
// 在一个构造函数中,去调用另外一个构造函数注意:是在冒号后面使用this调用
Person3.fromName(String name) : this(name, 0);
}
2.5. 常量构造方法
- 在某些情况下,传入相同值时,我们希望返回同一个对象,这个时候,可以使用常量构造方法
默认情况下,创建对象时,即使传入相同的参数,创建出来的也不是同一个对象
代码示例:
class Person4 {
String name;
Person4(this.name);
}
var p5 = Person4('wang');
var p6 = Person4('wang');
print(identical(p5, p6)); // 使用identical(对象1, 对象2)函数来判断两个对象是否是同一个对象 false
- 如果将构造方法前加const进行修饰,那么可以保证同一个参数,创建出来的对象是相同的 这样的构造方法就称之为常量构造方法
拥有常量构造方法的类中,所有的成员变量必须是final修饰的
为了可以通过常量构造方法,创建出相同的对象,使用const关键字
如果是将结果赋值给const修饰的标识符时,const可以省略
代码示例:
class Person5 {
final String name;
const Person5(this.name);
}
var p7 = const Person5('wang');
var p8 = const Person5('wang');
print(identical(p7, p8)); // true
2.6.工厂构造方法
- Dart提供了factory关键字, 用于通过工厂去获取对象
代码示例:
class Person6 {
String name;
static final Map<String, Person6> _cache = <String, Person6>{};
factory Person6(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final p = Person6._internal(name);
_cache[name] = p;
return p;
}
}
Person6._internal(this.name);
}
var p9 = Person6('wang');
var p10 = Person6('wang');
print(identical(p9, p10)); // true
3.setter和getter
- Dart中类定义的属性是可以直接被外界访问的 如果我们希望监控这个类的属性被访问的过程,这个时候就可以使用setter和getter了
代码示例:
class Cat {
String color;
String get getColor {
return color;
}
set setColor(String color) {
this.color = color;
}
Cat(this.color);
}
final cat = Cat('yellow');
cat.setColor = 'black';
print(cat.getColor); // black
4.类的继承
- 面向对象的其中一大特性就是继承,继承不仅仅可以减少我们的代码量,也是多态的使用前提
Dart中的继承使用extends关键字,子类中使用super来访问父类
父类中的所有成员变量和方法都会被继承,但是构造方法除外
子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写
子类的构造方法在执行前,将调用父类的无参默认构造方法(没有参数且与类同名的构造方法)
如果父类没有无参默认构造方法,则子类的构造方法必须在初始化列表中通过super显式调用父类的某个构造方法
代码示例:
class Animal {
int age;
Animal(this.age);
run() => print('奔跑');
}
class Person extends Animal {
String name;
Person(String name, int age)
: name = name,
super(age);
@override
run() {
print('$name在奔跑');
}
@override
String toString() {
return 'name = $name, age = $age';
}
}
Person p = Person('wang', 28);
p.run(); // wang在奔跑
print(p); // name = wang, age = 28
5.抽象类
- 继承是多态使用的前提 所以在定义很多通用的调用接口时, 通常会让调用者传入父类,通过多态来实现更加灵活的调用方式. 但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,我们可以定义为抽象方法
抽象方法在Dart中没有具体实现的方法(没有方法体)
抽象方法,必须存在于抽象类中 抽象类是使用abstract声明的类
抽象类不能实例化 抽象类中的抽象方法必须被子类实现
抽象类中的已经被实现方法, 可以不被子类重写
代码示例:
abstract class Shape {
getArea();
}
class Circle extends Shape {
double r;
Circle(this.r);
@override
getArea() {
return r * r * pi;
}
}
class Reactangle extends Shape {
double w;
double h;
Reactangle(this.w, this.h);
@override
getArea() {
return w * h;
}
}
6.隐式接口
- Dart中的接口比较特殊, 没有一个专门的关键字来声明接口。默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现(因为Dart不支持多继承)
代码示例:
abstract class Runner {
run();
}
abstract class Flyer {
fly();
}
// implements的方式要求必须对其中的方法进行重新实现
class SuperMan implements Runner, Flyer {
@override
run() {
print('超人在奔跑');
}
@override
fly() {
print('超人在飞');
}
}
var superMan = SuperMan();
superMan.run(); // 超人在奔跑
superMan.fly(); // 超人在飞
7.Mixin混入
- 在通过implements实现某个类时,类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)。但是某些情况下,一个类可能希望直接复用之前类的原有实现方案 但是Dart只支持单继承,那么意味着你只能复用一个类的实现
Dart提供了另外一种方案: Mixin混入的方式,除了可以通过class定义类之外,也可以通过mixin关键字来定义一个类,只是通过mixin定义的类用于被其他类混入使用,通过with关键字来进行混入
代码示例:
mixin Runner {
run() {
print('在奔跑');
}
}
mixin Flyer {
fly() {
print('在飞翔');
}
}
class SuperMan with Runner, Flyer {
@override
run() {
print('superMan在奔跑');
}
}
var superMan = SuperMan();
superMan.run(); // superMan在奔跑
superMan.fly(); // 在飞翔
8.类成员和方法
- Dart中我们使用static关键字来定义类成员和方法
代码示例:
class Student {
String name;
int grade;
static String time;
study() {
print('$name在学习');
}
static gotoClass() {
print('去上课');
}
}
var student = Student();
student.name = 'wang';
student.grade = 4;
student.study(); // wang在学习
Student.time = '10点';
Student.gotoClass(); // 去上课
9.枚举类型
-
枚举也是一种特殊的类, 通常用于表示固定数量的常量值
枚举使用enum关键字来进行定义
枚举的属性:
index: 用于表示每个枚举常量的索引, 从0开始
values: 包含每个枚举值的List
代码示例:
enum Colors {red, green, blue}
print(Colors.red); // Colors.red
print(Colors.red.index); // 0
print(Colors.green.index); // 1
print(Colors.blue.index); // 2
print(Colors.values); // [Colors.red, Colors.green, Colors.blue]
网友评论