类
构造函数
通过创建一个与其类同名的函数来声明构造函数 (另外,还可以附加一个额外的可选标识符,如 命名构造函数 中所述)。 下面通过最常见的构造函数形式, 即生成构造函数, 创建一个类的实例:
class Point {
num x, y;
Point(num x, num y) {
// 还有更好的方式来实现下面代码,敬请关注。
this.x = x;
this.y = y;
}
}
通常模式下,会将构造函数传入的参数的值赋值给对应的实例变量, Dart 自身的语法糖精简了这些代码:
class Point {
num x, y;
// 在构造函数体执行前,
// 语法糖已经设置了变量 x 和 y。
Point(this.x, this.y);
}
默认构造函数
在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。
构造函数不被继承
子类不会继承父类的构造函数。 子类不声明构造函数,那么它就只有默认构造函数 (匿名,没有参数) 。
命名构造函数
使用命名构造函数可为一个类实现多个构造函数, 也可以使用命名构造函数来更清晰的表明函数意图:
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x = 0;
y = 0;
}
}
切记,构造函数不能够被继承, 这意味着父类的命名构造函数不会被子类继承。 如果希望使用父类中定义的命名构造函数创建子类, 就必须在子类中实现该构造函数。
调用父类非默认构造函数
默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参数)。 父类的构造函数在子类构造函数体开始执行的位置被调用。 如果提供了一个 initializer list (初始化参数列表), 则初始化参数列表在父类构造函数执行之前执行。 总之,执行顺序如下:
- initializer list (初始化参数列表)
- superclass’s no-arg constructor (父类的无名构造函数)
- main class’s no-arg constructor (主类的无名构造函数)
如果父类中没有匿名无参的构造函数, 则需要手工调用父类的其他构造函数。 在当前构造函数冒号 (:) 之后,函数体之前,声明调用父类构造函数。
下面的示例中,Employee 类的构造函数调用了父类 Person 的命名构造函数。
class Person {
String firstName = "";
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父类没有默认的构造函数
//你必须调用其他的构造函数
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
//还可以用方法的形式传递参数
Employee.fromJson2(Map data) : super.fromJson(getDefaultData()) {
}
static Map getDefaultData() {
return {"name": "bob", "age": "10", "address": "china"};
}
初始化列表
除了调用超类构造函数之外, 还可以在构造函数体执行之前初始化实例变量。 各参数的初始化用逗号分隔。
// 在构造函数体执行之前,
// 通过初始列表设置实例变量。
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
重定向构造函数
有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。 重定向构造函数的函数体为空, 构造函数的调用在冒号 (:) 之后。
class Point {
num x, y;
// 类的主构造函数。
Point(this.x, this.y);
// 指向主构造函数
Point.alongXAxis(num x) : this(x, 0);
}
常量构造函数
如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。 为此,需要定义一个 const 构造函数, 并且声明所有实例变量为 final。
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工厂构造函数
当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。
以下示例演示了从缓存中返回对象的工厂构造函数:
class Logger {
final String name;
bool mute = false;
// 从命名的 _ 可以知,
// _cache 是私有属性。
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
抽象方法
实例方法, getter, 和 setter 方法可以是抽象的, 只定义接口不进行实现,而是留给其他类去实现。 抽象方法只存在于 抽象类 中。
定义一个抽象函数,使用分号 (;) 来代替函数体:
abstract class Doer {
// 定义实例变量和方法 ...
void doSomething(); // 定义一个抽象方法。
}
class EffectiveDoer extends Doer {
void doSomething() {
// 提供方法实现,所以这里的方法就不是抽象方法了...
}
}
调用抽象方法会导致运行时错误。
抽象类
使用 abstract 修饰符来定义 抽象类 — 抽象类不能实例化。 抽象类通常用来定义接口,以及部分实现。 如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数 来实现。
抽象类通常具有 抽象方法。 下面是一个声明具有抽象方法的抽象类示例:
// 这个类被定义为抽象类,
// 所以不能被实例化。
abstract class AbstractContainer {
// 定义构造行数,字段,方法...
void updateChildren(); // 抽象方法。
//具体方法
void updateChildren2() {
print("1111");
}
}
//具体实现
class InstanceContainer extends AbstractContainer {
@override
void updateChildren() {
print("=====");
}
@override
void updateChildren2() {
super.updateChildren2();
print("33333");
}
}
隐式接口
每个类都隐式的定义了一个接口,接口包含了该类所有的实例成员及其实现的接口。 如果要创建一个 A 类,A 要支持 B 类的 API ,但是不需要继承 B 的实现, 那么可以通过 A 实现 B 的接口。
一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口要求的 API。 例如:
class Person1 {
// 包含在接口里,但只在当前库中可见。
final _name;
final String name2 = "";
// 不包含在接口里,因为这是一个构造函数。
Person1(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
String greet2(String who) => 'Hello, $who. I am $_name.';
}
class Person2 {
// 包含在接口里,但只在当前库中可见。
final address;
final String age = "";
// 不包含在接口里,因为这是一个构造函数。
Person2(this.address);
// 包含在接口里。
String sayHello(String who) => 'Hello, $who. my address is $address.';
String sayHello2(String who) => 'Hello, $who. my age is $age.';
}
// person 接口的实现。
class Impostor implements Person1, Person2 {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
@override
// TODO: implement name2
String get name2 => "name2";
@override
String greet2(String who) {
return "121212";
}
@override
// TODO: implement address
get address => "21212";
@override
// TODO: implement age
String get age => "598";
@override
String sayHello(String who) {
return "dsdsd";
}
@override
String sayHello2(String who) {
return "dsdsdddd";
}
}
String greetBob(Person1 person) => person.greet('Bob');
扩展类(继承)
使用 extends 关键字来创建子类, 使用 super 关键字来引用父类:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
重写类成员
子类可以重写实例方法,getter 和 setter。 可以使用 @override 注解指出想要重写的成员:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
变量
1.实例变量:
实例变量是属于对象的,每个对象都有独立的实例变量。它们在类的定义中直接声明,且可以通过对象访问。
class Car {
String model;
int year;
Car(this.model, this.year);
}
2.静态变量:
静态变量属于类而不是对象,它在类级别上共享。静态变量使用 static 关键字声明。
class Car {
static int totalCars = 0;
String model;
int year;
Car(this.model, this.year) {
totalCars++;
}
}
示例
void main() {
Car car1 = Car('Tesla Model S', 2020);
Car car2 = Car('BMW M3', 2021);
print(Car.totalCars); // 输出 2,表示已经创建了两个 Car 对象
}