6 - Dart 类

376 阅读5分钟

类是所有面向对象语言最重要的一个环节。首先需要对 Dart 的类进行一个比较全面的认知,才有助于我们学习和理解其中一些功能的设计:Dart 中的类是 单继承,且没有 方法重载

创建一个类

  • 方法 / 属性
    创建一个类,和 java 很像,定义属性,定义构造函数,在定义一些方法。实例化的时候, new 也不用写,直接按照构造参数传参,一个 Rect 实例就诞生了。

    class Rect{
      int width = 0;
      int height = 0;
      Rect(int width,int height){
        this.width = width;
        this.height = height;
      }
      int area()=>width * height;
    }
    
    var rect = Rect(10, 12);
    print(rect.area());
    
  • 静态方法和属性
    静态方法和属性很简单,加上 static 之后即可。

      class Rect{
        int width = 0;
        int height = 0;
        static demo = 10;
        Rect(int width,int height){
          this.width = width;
          this.height = height;
        }
        int area()=>width * height;
        static Area(int width, int height) => width * height;
      }
    
  • get and set
    java 不一样,我们默认的属性就能给外界访问,因此不需要写繁琐的 getset 方法。但是,如果你希望监听属性变化,getset 不失为一个很好的方案。

    class Rect{
      int width = 0;
      int height = 0;
      
      set setWidth(int width) {
        this.width = width;
      }
    
      int get getWidh {
        return this.width;
      }
    }
    

构造函数

  • 初始化列表
    大家都知道构造函数传递的参数一般都是给特定的属性的,因此,如果你在构造函数中没有其他的事情做,Dart 是提供了一个简化的写法: 初始化列表
    除了能简化构造函数的写法,它也能在构造函数的时候给一个 final 的属性赋值。 final 的值只能赋值一次。因此传统写法会报错:构造函数函数体执行的时候,这些属性已经给创建了。

    // 繁琐的传统写法
    class Rect{
      final int width; // final 的值只能赋值一次。因此传统写法会报错。
      int height = 0;
      Rect(int width,int height){
        this.width = width;
        this.height = height;
      }
    }
    // 简化写法
    class Rect{
      int width = 0;
      int height = 0;
      Rect(this.width,this.height);
    }
    
    // ru1
    
  • 命名构造函数
    由于 Dart 是没有方法重载的。但是不同的构造函数来创建类是实际开发必须的。因此有了这个: 命名构造函数
    个人认为, 命名构造函数 + 函数的命名参数 简直就是 object-c 的常规函数写法。虽然有点繁琐。但是在阅读代码的时候,能让人非常清晰,一读就通。
    同时也可以通过 重定向构造函数 来简化我们的 命名构造函数

    class Rect{
      int width = 0;
      int height = 0;
      Rect(this.width,this.height);
      // 命名构造函数
      //Rect.fromJson(Map json){
      //  this.width = json["width"];
      //  this.width = json["height"];
      //}
      // 重定向构造函数 简化赋值过程
      Rect.fromJson(Map json) : this(json["width"], json["height"]);
    }
    
    var rect = Rect.fromJson({"width": 1, "height": 2});
    
  • 工厂构造方法
    如果我们希望创建一个单例对象的时候,我们希望这个类在任何时候都能返回第一次创建的对象,因此我们就需要控制构造函数返回的对象,而在上面介绍的构造方法里面,我们都无法控制返回的对象,因此 工厂构造方法 就诞生了。

    class Singleton {
      static Singleton instance;
      Singleton() {}
      factory Singleton.shareInstance() {
        if (instance != null) {
          instance = Singleton();
        }
        return instance;
      }
    }
    

继承 (extends)

使用 extends 我们能把一个类的所有属性和方法都放进一个新的类里面,除了能在新的类加上自己特有的方法,还能通过 super 访问父类的方法,通过 @override 改写父类的方法。

class Animal {
  String name;
  Animal(this.name);
  void say() {
    print(this.name);
  }
}

class Cat extends Animal {
  Cat(String name) : super(name);

  @override
  void say() {
    print("my name is " + this.name);
  }
}

抽象类

abstract 定义的都是抽象类,而抽象类中,有只存在名字,而没有内容的方法,这种叫做抽象方法。

  • 抽象类无法实例化
  • 继承抽象类的子类必须把抽象类中的抽象方法给实现了。
  • 抽象类已经实现的方法不能通过 @override 修改了。
abstract class Animal {
  String name;
  Animal(this.name);
  void eat();
  void say() {
    print(this.name);
  }
}

class Cat extends Animal {
  Cat(String name) : super(name);

  @override
  void eat() {
    print(this.name + " eat fish");
  }
}

接口 (implements)

dart 中,所有的类都是接口。因此我们可以直接 implements 所有的类,和 extends 不一样,一旦 implements 了指定的类之后,必须把它的方法都实现一遍,而且不能调用 super。因为 super 是属于父类的。
由于很多时候我们使用接口都只是约定一个规范,让我们去调用指定的接口方法。因此在 dart 一般都是使用 抽象类 来做接口。这样就不要把实现了一遍的方法又实现一遍。

class Animal {
  String name;
  Animal(this.name);
  void say() {
    print(this.name);
  }
}

class Action {
  void eat() {
    print("eat");
  }
}

class Cat extends Animal implements Action {
  Cat(String name) : super(name);

  @override
  void say() {
    print("my name is " + this.name);
  }

  @override
  void eat() {
    print(this.name + " eat");
  }
}

混合 (mixin)

implements 其实只是使用了一个类的躯壳,每一个方法在新的类上都需要重新实现一遍,不能像 extends 能直接使用,但是 extends 只能是单继承。因此 混合 这个新的特性就在 dart 诞生了。

class Animal {
  String name;
  Animal(this.name);
  void say() {
    print(this.name);
  }
}

class Action {
  String action;
  void doSomeThing() {
    print(this.action);
  }
}

class Cat extends Animal with Action {
  Cat(String name) : super(name);

  @override
  void say() {
    print("my name is " + this.name);
  }
}

main(List<String> args) {
  var cat = Cat('cat');

  cat.say();
  cat.action = "eat";
  cat.doSomeThing();
}

继承,混合,接口的区别

在刚使用 dart 的时候,最大的困惑就是:with, extends 和 implements 的区别。

  • 从思想层面上说:
    • extends 是面向对象的常用做法,主要是为了把同类事物的特性提取出来,从而达到代码的复用,
    • with 的思想主要是组合,不仅仅是同类的事物有这些特性,我们可以把不同类的事物的相同逻辑提取出来使用。
    • implements 的思想就是约定,每一个接口就是一个个约定和规则,我们根据这些规则去实现自己的方法。
  • 从使用层面来说
    • extends 和一般的继承一样,把方法,属性都给新的类,新的类可以访问回去父类,可以选择是否改写父类的东西。继承之后,两个还是关联的
    • with 和继承很像,只是优先级永远低于继承:同名方法和属性,都以继承为准
    • implements 其实就是无论 implements 了什么类,你都可以把这个类当成抽象类,因为你得把它的方法都实现一遍