Dart2基础(八) - 类和枚举

目录

类的定义和使用

获取对象类型

实例变量

构造函数

子类构造函数

重定向构造函数

静态构造函数

工厂构造函数

实例方法

抽象类和方法

隐式接口

类的继承

重载运算符

枚举

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 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:

  1. initializer list(初始化列表)
  2. superclass’s no-arg constructor
  3. main class’s no-arg constructor

如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须显式调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:(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是给类添加新的特性的方式,也是一种重用类代码的一种方式。

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
}

 

你可能感兴趣的:(dart2,教程)