Dart是面相对象编程语言,对象都是由类创建的,所有类都是由Object类派生出来的子类,除了Object, 所有类只有一个父类(即只能继承一个父类)。
尽管Dart语言中一个类只能继承一个父类,但是Dart语言提供了mixins机制,可以复用多个类,达到类似多继承的效果。
1.类的定义
Dart语言类定义跟Java非常相似,下面看一个例子.
class Person {
// 定义类成员属性,默认类的成员属性和方法都是共有的,类似java的public
String name;
// 以下划线 ( _ ) 开头命名的属性代表私有(private)成员属性,类方法也是类似的规则。
int _age;
// 跟类名同名的方法,为构造方法
// 这里自定义了一个携带参数的构造方法。
// 如果我们没有自定义构造方法,会自动生成一个不带参数的默认构造方法
Person(String name, int age) {
// 因为参数名和类属性名同名,可以使用this引用当前对象
this.name = name;
// 可以忽略this关键字,直接引用类成员
_age = age;
}
// 定一个public的方法
String greet(String who) => 'Hello, $who. I am $name, my age is $_age !';
}
通过上面的例子,我们基本知道Dart类定义语法结构,跟java非常类似,关于类方法(函数)的定义跟普通函数差不多,区别就是类方法定义在类内部,而且可以引用类的成员(属性和方法)。
关于函数的定义的细节,可以参考函数章节。
2.实例化变量
下面是类的基本用法。
// 实例化一个Person类的对象,这里使用的是我们前面自定义的构造方法初始化一个对象
var p = Person("tizi", 20);
// 通过点 (.) 调用对象的实例, 这里调用对象的方法
var ret = p.greet("dacui");
// 这是错误的,因为_age是私有属性,不能访问。
// var age = p._age;
Dart语言为我们提供了一个语法特性 ?. (问号点),用来访问对象的成员属性或者方法,他跟 点( . ) 的区别是它会判断对象是否为null ,如果对象为null就不执行属性访问操作。
// 例子
// 定义一个null对象
var p;
// 通过 ?. 访问对象的属性, 这里是不会报错的,tmp变量最终得到的是null。
// 因为p是null, 所以不会真的访问name成员。
var tmp = p?.name;
这个语法特性,主要目的是避免调用null变量的成员,而引发错误。
3.构造方法
如果我们没有自定义一个构造方法,会自动生成一个不带参数的默认构造方法。
// 这个类会生成默认的构造方法
class Person {
String name;
}
// 通过默认构造方法实例化对象
var p = Person();
3.1. 自定义构造方法
class Point {
num x, y;
Point(num x, num y) {
// 通过this访问成员属性,当然一般除非出现命名冲突,否则可以忽略this
this.x = x;
this.y = y;
}
}
对于构造方法中,简单的赋值操作,Dart语言提供了更简洁的语法。
class Point {
num x, y;
// 直接将构造方法的第一个参数赋值给this.x, 第二个参数赋值给this.y
Point(this.x, this.y);
}
3.2.初始化参数列表
Dart语言还为构造方法提供了 参数初始化列表 的语法,用于初始化对象参数。
class Point {
num x, y;
// 冒号 : 后面的表达式就是参数初始化列表,每个表达式用逗号分隔。
Point(int x, int y) : this.x = x, this.y = y {
// 使用参数初始化列表初始化对象属性,这里如果没有别的初始化工作要做,可以是空的。
}
}
3.3.命名构造方法
在Dart语言中可以使用命名构造方法语法,创建多个构造方法。
命名构造方法语法格式: 类名.构造方法名(参数列表)
// 定义类
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造方法origin
Point.origin() {
x = 0;
y = 0;
}
}
// 使用命名构造方法实例化对象
var p = Point.origin();
上面的例子也可以改写为:
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造方法origin
// 这里通过 参数初始化列表,直接通过this调用上面的构造方法,传入两个参数0,初始化对象。
Point.origin() : this(0, 0);
}
3.4.factory 构造方法
Dart语言还提供了一个特殊的构造方法,类似设计模式中的工厂模式,用来创建对象。
提示:factory 构造方法只能访问静态属性和静态成员方法,因此不能访问this引用。
下面看个factory构造方法的例子。
// 定义个日志类
class Logger {
final String name;
bool mute = false;
// 定义一个私有的_cache属性,用来保存创建好的Logger对象
static final Map<String, Logger> _cache =
<String, Logger>{};
// 注意这个构造方法,前面使用了factory关键字修饰,这代表这个构造方法是一个工厂构造方法
// 工厂构造方法不会每次都创建一个新的Logger对象
factory Logger(String name) {
// 根据name判断缓存的Logger对象是否存在
if (_cache.containsKey(name)) {
// 返回缓存的Logger对象
return _cache[name];
} else {
// 如果没有缓存,则调用命名构造方法_internal创建一个Logger对象
final logger = Logger._internal(name);
// 根据name缓存logger
_cache[name] = logger;
// 返回新的Logger对象
return logger;
}
}
// 注意这个是一个私有的命名构造方法。
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
使用factory工厂构造方法
// 根据UI关键词,返回一个Logger对象,每次根据同样的参数都可以返回同一个对象
var logger = Logger('UI');
logger.log('Button clicked');
4.继承一个类
通过extends关键字继承一个类。
// 定一个父类
class Television {
void turnOn() {
print("i am father");
}
}
// 定义一个子类, 继承Television父类
class SmartTelevision extends Television {
// 重写父类的方法
void turnOn() {
print("i am son");
}
}
使用子类
var s = SmartTelevision();
s.turnOn();
输出
i am son
继承一个类,将拥有父类的所有公有属性和方法,如果出现相同的方法,则子类的方法优先被调用,当然子类也可以通过super访问父类成员。
class SmartTelevision extends Television {
void turnOn() {
// 通过super访问父类成员
super.turnOn();
}
}
5.抽象类和抽象方法
抽象类就是不能实例化的类,一般都是用来定义接口,抽象方法就是没有实现的方法。
例子:
// 使用abstract关键词修饰的类,就是抽象类
abstract class Doer {
// 抽象类跟普通类一样,可以定义成员变量,成员方法。
// 定义个抽象方法,这个方法我们没有实现具体的功能。
void doSomething();
}
// 继承抽象类Doer
class EffectiveDoer extends Doer {
// 实现抽象类的抽象方法
void doSomething() {
print("实现了抽象方法doSomething");
}
}
继承抽象类,子类必须要实现所有抽象方法,否则会报错。
6.接口
在Dart语言中接口的定义通常是由抽象类实现的,上面已经介绍过了,这里介绍Dart语言的隐式接口,默认情况每一个类都隐含一个包含所有公有成员的接口定义。
如果我们想实现一个类A的接口,而不想继承A,可以使用implements关键词实现类A的接口。
例子:
// 定义一个类
class Person {
// 包含在隐式接口里面
String name;
// 构造方法不包含在隐式接口里面
Person(this.name);
// 包含在隐式接口里面
String greet(String who) => 'Hello, $who. I am $name.';
}
// 实现 Person 的接口,而不继承他
class Impostor implements Person {
// 接口属性也需要实现
String name;
// 实现接口的方法
String greet(String who) => 'Hi $who. Do you know who I am?';
}
// 定义测试函数,用来调用greet方法
String greetBob(Person person) => person.greet('Bob');
void main() {
// 实例化Person, 打印结果
print(greetBob(Person('Tizi')));
// 实例化Impostor, 打印结果
print(greetBob(Impostor()));
}
输出
Hello, Bob. I am Tizi.
Hi Bob. Do you know who I am?
Dart语言允许实现多个接口。
// 例子,实现多个类的接口,使用逗号分隔多个类。
class Point implements Comparable, Location {...}