目录
类的定义和使用
获取对象类型
实例变量
构造函数
子类构造函数
重定向构造函数
静态构造函数
工厂构造函数
实例方法
抽象类和方法
隐式接口
类的继承
重载运算符
枚举
mixins
类变量和方法
Dart2中的类都是继承于类Object。Dart2中每一个类都有有一个超类(Object除外),但是一个类的主体可以在多个类层次中使用。
Dart2中使用关键字class定义类,用.和?.调用类的成员。?.的用法是当对象不为空时,调用成员,为空时,跳过不调用。
class Animal{
String name;
// 构造函数
Animal(String name){
this.name = name;
}
run(){
print("${this.name} is running");
}
}
main(List args) {
// 使用构造函数创建对象
var cat = Animal("cat");
// 也可以用new来创建对象,new从Dart2开始是可选的
var dog = new Animal("dog");
cat.run();
dog.run();
// cat不是null时,name赋予新值
cat?.name = "small cat";
cat.run(); // small cat is running;
Animal nullAnimal;
// nullAnimal是null,如果直接使用nullAnimall.name,会对象为空的报错
nullAnimal?.name = "null";
}
每一个对象都有一个runtimeType的属性,通过这个属性可以获取到该对象的类型(Type的对象)。
class Animal(){}
main(){
// 返回的是一个Type的对象
print(Animal().runtimeType); // Animal
}
每一个实例变量都有一个隐式的Get方法,如果变量不是final类型的,那么实例变量会有一个隐式的Set方法。
如果一个实例变量有初始值,那么它的值在构造方法生效之前,就已经被赋予。
如果想重写Get和Set方法,可以用关键字get和set实现。
Dart2中以_开头的变量和方法,都是私有的。
// 官网实例代码
class Rectangle {
num left = 10;
num _left_width = 12; // 私有变量
// 定义多个变量
num top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right and bottom.
// 需要注意的是,get和set需要成对出现
// 重写get
num get right => left + width;
// 重写set
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
构造函数的定义,可以通过函数名为ClassName和ClassName.identifier(命名的构造函数 Named Constructors)定义。
通过命名的构造函数可以轻松实现多个不同的构造函数。
如果类没有构造函数,那么默认会有一个没有参数的构造函数。
class Animal{
String name;
String color = "yellow";
Animal(String name){
this.name = name;
}
// 上面的构造函数,可以简写为
Animal(this.name);
Animal.createInstance(Map values){
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
main(List args) {
var animal = Animal.createInstance({"name": "dog"});
print(animal.name);
}
父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。默认情况下,子类只能调用父类的无名,无参数的构造函数。父类的无名构造函数会在子类的构造函数前调用。如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:
如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须显式调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:
(colon) 分割。
在开发状态下,初始化列表可以使用assert表达式。
class Animal{
String name;
String color = "yellow";
Animal.createInstance(Map values){
print("in animal");
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
// extends 继承的关键字
class Bird extends Animal{
String name = "blue bird";
String desc;
Bird.createInstance(Map values):
desc = name, // 初始化列表,注意这里没有this
super.createInstance(values){ // 父类方法调用使用super
print("in bird");
}
}
main(List args) {
var bird = Bird.createInstance({
"name": "bird"
});
print(bird.name); // bird
print(bird.desc); // blue bird
}
所谓重定向构造函数,是指在一个构造函数中指向另一个构造函数,但是重定向构造函数的函数体是空的。以官网代码为例。
class Point {
num x, y;
// 主构造函数
Point(this.x, this.y);
// 重定向构造函数
Point.alongXAxis(num x) : this(x, 0);
}
如果类的对象不会有任何变化,那么可以让这些对象是编译时常量。通过创建静态构造函数,并且所有成员属性是final的来实现。
静态构造函数,并不是都会返回一个编译时常量。
class Animal{
final String name;
final String color;
// 静态构造函数
const Animal(this.name, this.color);
}
main(List args) {
var cat = const Animal("cat", "yellow"); // 常量
var cat2 = const Animal("cat", "yellow");
var cat3 = Animal("cat", "yellow"); // 非常量
// 构造两个相同的编译时常量,实际上是一个常量
print(identical(cat, cat2)); // true
print(identical(cat, cat3)); // false
// 在常量上下文中,可以省略构造函数或文字前面的常量
const cats = const {
'bluscat': const [const Animal("cat", "blue")],
'yellowcat': const [const Aminal("cat", "yellow")],
};
// 上面的代码可以写成下面这样,这是Dart2的新特性
const cats2 = {
'bluscat': [Animal("cat", "blue")],
'yellowcat': [Aminal("cat", "yellow")],
};
}
使用关键字final实现工厂构造函数。工厂构造函数可以让你不必要每次都创建一个新的对象,就好像是有一个缓存,缓存了老的对象。
工厂构造函数中不能使用this。
下面是官网的实例:
class Logger {
final String name;
bool mute = false;
// 私有变量
static final Map _cache =
{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
void main() {
var p1 = Logger("Part1");
p1.log("this is part1");
var p2 = Logger("Part2");
p2.log("this is part2");
}
实例方法是对象中的函数。实例方法中可以使用实例变量和this。
class Animal(){
String name;
Animal(this.name);
// 实例方法
void run(){
print("${this.name} is running");
}
}
使用关键字abstract声明的类,就是抽象类。抽象方法是没有实现体的方法,抽象方法只能存在于抽象类中。
实例方法、Setter和Getter方法都可以是抽象方法。
抽象类不能被实例化。如果想要抽象类进行实例化,可以用工厂构造函数实现。
抽象类通常用来定义接口。
abstract class Animal{
String color = "blue";
// 抽象方法
run();
}
class Bird extends Animal{
String name;
Bird(this.name);
// 抽象方法的实现
run(){
print("A ${this.color} ${this.name} is flying");
}
}
main(List args) {
var bird = Bird("tom");
bird.run();
}
Dart2中的每一个类都有一个隐式的接口,这个隐式的接口包含所有的实例成员和实现的接口的实例成员。
如果你想创建一个类A支持类B的API函数,但是不想继承B的实现,则类A需要继承类B的接口。
继承接口的关键字是implements, 一个类可以继承多个接口。
class Animal{
String _name; // 在接口中,但是是私有的,所以只能在本类中使用
Animal(this._name); // 构造函数不在接口中
run(){
print("${this._name} is running");
}
}
// 实现Animal的接口,如果还有其他的接口,可以用,分割 implements Animal, ...
class Bird implements Animal{
String _name;
Bird(this._name);
run(){
print("${this._name} can not run");
}
}
runningAnimal(Animal animal){
animal.run();
}
main(List args) {
runningAnimal(Animal("dog")); // dog is running
runningAnimal(Bird("bird")); // bird can not run
}
使用extends来继承父类,super来指定父类。
子类可以重载父类的实例方法、Setter和Getter,使用@override注释重新的方法。
重载noSuchMethod() 方法,可以在对象视图调用不存在的方法或者变量时调用。但是,你不能调用一个不存在的方法,除非是以下情况的一种:
1. 一个静态类型的dynamic的变量;
2. 接收方有一个定义未实现的方法的静态类型(抽象的也可以),并且接收方是dynamic并且有一个noSuchMethod()的实现,与类Object中的实现不同。
class Animal{
String name;
String color = "yellow";
Animal.createInstance(Map values){
print("in animal");
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
// extends 继承的关键字
class Bird extends Animal{
String name = "blue bird";
String desc;
Bird.createInstance(Map values):
desc = name, // 初始化列表,注意这里没有this
super.createInstance(values){ // 父类方法调用使用super
print("in bird");
}
@override
run(){
pirnt("${this.name} can not run");
}
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
可以重载的运算符有:
< |
+ |
| |
[] |
> |
/ |
^ |
[]= |
<= |
~/ |
& |
~ |
>= |
* |
<< |
== |
– |
% |
>> |
class Person{
String name;
Person(this.name);
// 重载 +
Person operator + (Person p){
return Person(this.name + " *** " + p.name);
}
}
main(List args) {
var man = Person("man");
var woman = Person("woman");
var person = man + woman;
print(person.name); // man *** woman
}
枚举类型是一种特殊的类,通常用来表示相同类型的一组常量。使用关键字enum定义枚举。
枚举的每一个值都有一个index属性,index从0开始计数。
枚举不能被继承,不能创建实例。
enum Animal {
cat,
dog,
bird
}
main(List args) {
print(Animal.dog.index); // 1
// 获取所有枚举值
List animals = Animal.values;
Animal dog = Animal.dog;
switch (dog) {
case Animal.cat:
print("animal is cat");
break;
case Animal.dog:
print("animal is dog");
break;
default:
print("which animal?");
}
// animal is dog
}
mixins是给类添加新的特性的方式,也是一种重用类代码的一种方式。
mixins的关键字是with。详细的使用,可以查看文档。
with不能单独使用,必须跟着extends使用。
class Fly{
fly(){
print("flying");
}
}
class Animal{}
class Bird extends Animal with Fly{
}
main(List args) {
var bird = Bird();
bird.fly(); // flying
}
使用关键字static,声明类变量和类方法
class Animal{
static var name; // 类变量
static const color = "yellow"; // 类常量
// 类方法
static run(){
print("running");
}
}
main(List args) {
Animal.name = "dog";
print(Animal.name); // dog
Animal.run(); // running
}