声明类

Dart是纯面向对象的语言,每个对象都类的实例,所有的类都继承自Object;Dart提供了丰富的OOP语法,比如基于Mixin的继承,和Java不相上下。

使用class声明一个类,并给它添加属性和方法,跟Java几乎一样:

class User{
  String name; //默认为null
  int age; //默认为null

  void say(){
    print("hello, name: $name  age: $age");
  }
  //模块私有方法  
  void _say2(){
  }  
}

接下来创建类对象,并访问属性和方法:

var user = User();//和Kotlin一样,没有new
user.name = "lxj";
user.age = 20;
user.say();

我们并没有给上面的类显式声明一个构造,Dart会为它创建一个默认无参构造。

构造方法

有类必然少不了构造。我们可以这样声明一个构造,并给构造传参:

class User{
  String name;
  int age;
  User(String name, int age){
    this.name = name;
    this.age = age;
  }
}

可以看出仍然是Java流派的写法,但这并不是最好的写法,在构造中给字段赋值太过常见,因此Dart提供了一种简写:

class User{
  String name;
  int age;
  User(this.name, this.age); //注意省略了大括号,因为构造中是空代码块,此时可以省略大括号
}

现在可以通过构造来创建对象了:

var user = User("lxj", 20);

Dart的构造仍然可以使用命名参数,所以无需像Java那样重写多个类似的无意义的构造方法:

class User{
  String name;
  int age;
  User({this.name = "lxj", this.age = 20});
}
print(User().name);//lxj

为了更清晰表达含义,Dart还提供了一种命名构造,使用类名.方法名的方式来声明命名构造:

class User{
  String name;
  int age;
  User({this.name = "lxj", this.age = 20});

  User.vip(){
    name = "王思聪";
    age = 18;
  }
  User.oldUser(){
    age = 60;
  }
}
//使用命名构造创建对象
print(User.vip().name);
print(User.oldUser().age);

命名构造看起来像静态方法调用,但其实并不是,在实际开发中我们可以定义很多命名构造来创建不同需求的对象。

构造初始化器

可能翻译的不准,官方文档叫Initializer list,作用是在自己和父类构造执行之前初始化实例的变量,这点借鉴了C++的语法。来看看用法:

class User {
  String name;
  int age;
  User.fromJson(Map<String, Object> json)
      : name = json['name'], //构造初始化器
        age = json['age'] {
    //do something...
  }
}

相当于可以在构造执行前来初始化一些数据,但把同样的代码写在构造中区别也不大,一般可以用来在创建实例之前对数据进行检查。在有构造初始化器时构造方法的执行顺序是:

  1. 先执行构造初始化器
  2. 执行父类无参构造
  3. 执行自己的无参构造

静态属性和方法

Dart声明静态的方式和Java很像。

class User{
  //静态属性
  static const Address = "China";
  //静态方法
  static void printAddress(){
    print(Address);
  }
}
//访问静态属性和方法
print(User.Address);
User.printAddress();

Factory构造

工厂构造可以实现单例模式。一般实现单例的第一步是把构造函数私有化,让别人无法创建对象。在Dart中一般通过添加一个私有的命名构造来实现:

class User  {
  String name;
  //私有的命名构造,命名构造的方法名是任意的,比如可以叫User._abc()  
  User._private(this.name);
}

由于我们给类增加了命名构造,Dart就不会生成默认无参构造;而命名构造又是私有的,只能本文件中使用,别的文件无法使用,因此无法创建对象。但这其实不是严格的构造私有化,因为我们还是可以在本文件的其他地方调用User._private()方法创建出多个对象的。

在构造方法前添加factory关键字就变成了工厂构造,工厂构造函数要求必须返回当前类对象,而且不能访问属性:

class User {
  String name;
  factory User(String name) {
    //不能访问属性  
    return User._private(name);
  }
  User._private(this.name);
}

来测试下是否有单例效果了:

print(User("lxj")==User("lxj")); //false

结果竟然不是单例的,其实这个工厂构造有点鸡肋,它需要我们自己实现单例的逻辑:

class User {
  String name;
  static User _instance = null;
  factory User(String name) {
    if(_instance==null){
      _instance = User._private(name);
    }
    return _instance;
  }
  User._private(this.name);
}
print(User("lxj")==User("lxj")); //true

其实上面的代码我们完全可以像Java那样使用静态方法来实现单例:

class User {
  String name;
  static User _instance = null;
  static User getInstance(String name){
    if(_instance==null){
      _instance = User._private(name);
    }
    return _instance;
  }
  User._private(this.name);
}
print(User.getInstance("lxj")==User.getInstance("lxj"));//true

2个版本比较一下,发现工厂构造的好处是使用起来更加自然,创建对象的同时顺便执行单例逻辑。但个人觉得还是有点鸡肋!

Getters and setters

Getter和Setter分别用来对类的属性提供读取和访问的能力。Dart的每个非final属性都会隐式拥有setter和getter。比如:

class Rect{
  num left, top, width, height;
  Rect.empty(){
    left = 0;
    top = 0;
    width = 0;
    height = 0;
  }
}
var rect = Rect.empty();
rect.left = 10;//执行left的setter
print(rect.left);//执行left的getter

Dart中的Getter和Setter通常用来增加一些可计算的属性,语法如下:

class Rect{
  num left, top, width, height;
  Rect.empty(){
    left = 0;
    top = 0;
    width = 0;
    height = 0;
  }
  //增加一个可计算属性right 
  num get right => left + width;
  set right(num value) => left = right - width;
}
var rect = Rect.empty();
rect.left = 10;//执行left的setter
rect.width = 30; 
print(rect.right);//执行right的getter

继承和抽象类

Dart的继承也和Java很像。

class People {
  int age;
  People(this.age);
  void eat(){
    print('i can eat');
  }
}

class User extends People {
  //由于父类没有无参构造,所以子类的构造必须调用父类的构造
  User(int age) : super(age);
  User.vip(int age) : super(age) {
  }
}
User(10).eat();//i can eat

抽象类和Java也很像:

abstract class People {
  int age;
  void eat();
}
class User extends People {
   //注解可以省略
  void eat() {
    print('eat eat...');
  }
}

隐式接口

Dart中没有像interface之类的关键字来声明接口,在我们声明一个类的时候同时也声明了一个包含这个类所有的成员(变量和方法)的接口。如果我们想创建一个类A,让它拥有B的所有API但是不要B的实现,那么让AimplementsB就可以了。

咋一看好像并没有什么乱用,如果我想让类A拥有B的的所有方法不要B的实现,那和在A中自己声明有什么区别吗?

但这种隐式接口的设计借鉴了Python的duck-type设计,duck-type的意思是只要一个东西看起来像鸭子(比如有鸭嘴和2条腿),那我就认为它是鸭子。Dart中只要一个类A看起来像B(拥有B的成员),那我就认为它是B,这很大程度提高程序的灵活性。假设一个方法只接收B类型参数,那其实只要让A实现B就能实现多态;而且每个类既能当类用,又能当接口用,省代码。

//同时隐式创建了接口Animal
class Animal {
  int age;//在接口中存在
  Animal(this.age); //注意:构造方法不在接口中
  void eat() { //在接口中存在
    print('eat');
  }
}
class Dog implements Animal{
  
  int age;

  
  void eat() {
    print("我喜欢吃骨头");
  }
}
//声明一个方法,接收Animal类型
void foo(Animal animal){
  animal.eat();
}
//多态调用
foo(Dog());

Mixin继承

Mixin(混合)是另外一种可以用其他类代码的方式。Dart默认不支持多继承,但是用Mixin就可以实现多继承,重用多个类的代码。

定义Mixin:

mixin Doctor {
  var canSaveLife = true;
  void saveLife(){
    print('save life');
  }
}
mixin Coder {
  var is996 = true;
  void writeCode(){
    print('write code with 996!!!');
  }
}

每个Mixin代表一种能力,现在每个类都可以拥有上面2种能力。

class Women with Doctor, Coder { }
class Man with Doctor, Coder { }
Women().writeCode();
Man().saveLife();

Mixin还支持类型限定,比如限定只能实现了某个类型才能使用,使用on关键字来指定限定类型:

//限定只有实现了People类型的才能使用
mixin Doctor on People{
  var canSaveLife = true;
  void saveLife(){
    print('save life');
  }
}
class People{}
class Women extends People with Doctor, Coder { }
class Man with Coder { } //Man未实现People,所以不能使用Doctor
更新时间: 4/27/2019, 8:54:35 PM