#

#

Dart是一种基于类和mixin继承机制的面向对象的语言.每个对象都是一个类的实例,所有的类都继承于Object

# 定义和实例化

定义一个类

class Person {}

实例化一个对象

var p = new Person();
print(p);  // Instance of 'Person'

所有的实例变量都生成隐式的getter方法,非final的实例变量同样会生成隐式setter方法

void main() {
  var p = new Person();
  p.name = 'zhangsan';
  print(p.name);
}
class Person {
  String name;
}

这里在 p.name = 'zhangsan'的时候,就调用了setter方法, print(p.name)调用了getter方法

# 构造函数

在没有声明构造函数的情况下,会有一个默认的构造函数, 默认构造函数没有参数并会调用父类的无参构造函数 通过创建一个与其类名相同的函数来声明构造函数或者附加一个额外标识符(即命名构造函数)

void main() {
  var p = new Person('zhangsan');
  print(p.name);  // zhangsan
}
class Person {
  String name;
  Person(String name){
    this.name = name;  // 这里的this指的是当前实例
  }
}

Dart具有精简构造函数写法的语法糖.通常情况下,我们都会这么做,只有当存在命名冲突的时候,才建议使用this.一般采用下面的写法

void main() {
  var p = new Person('zhangsan');
  print(p.name);
}
class Person {
  String name;
  Person(this.name);
}

构造函数不会被继承,即子类不会继承父类的构造函数.所以若子类不声明构造函数,那么它就只有默认构造函数(匿名,无参)

# 命名构造函数

使用命名构造函数可为一个类实现多个构造函数,同时也可以更清晰的表明函数意图

void main() {
  var p = new Person('zhangsan');
  var p2 = new Person.nickname('xiaozhang');  //通过命名构造函数实例化一个对象
  print(p.name);  // zhangsan
  print(p2.name);  // xiaozhang
}
class  Person {
  String name;
  Person(this.name);
  Person.nickname(this.name);  // 命名构造函数
}

# 调用父类构造函数

默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参).父类的构造函数在子类构造函数体开始执行的位置被调用.若有初始化参数列表,则初始化参数列表在父类构造函数之前执行

初始化参数列表 => 父类默认构造函数 => 主类的无名构造函数

如果父类没有默认构造函数,那么就需要子类手动调用父类的其他构造函数,在当前构造函数冒号:之后,函数体之前,声明调用父类构造函数

void main(){
  var s = new Student('zhangsan','math');
  print(s);
  print(s.name);
  print(s.job);
}

class Person {
  String name;
  Person(this.name);
}

class  Student extends Person {
  String job;
  Student(String name,String job):super(name){
    this.job = job;
  }
}

# 初始化列表

可以初始化实例变量,位于函数体之前,各参数用逗号隔开

void main(){
  var p = new Person('zhangsan',12);
  print(p.name);
  print(p.age);
}

class Person {
  String name;
  int age;
  Person(String name, int age)
      :name = name + ' shuai',
       age = age;
}

在开发阶段,使用assert来验证输入的初始化列表

class Person {
  String name;
  int age;
  Person(String name, int age)
    :assert(age >= 18);
}

使用初始化列表设置final修饰的常量

class Person {
  final String name;
  final String sex;
  Person(name, sex)
    :name = name,
     sex = sex;
}

# 重定向构造函数

重定向到同一个类的其他构造函数去,重定向构造函数体为空,构造函数的调用是在冒号(:)之后

void main(){
  var p = new Person.nav('zhangsan');
  print(p.name);
  print(p.age);
}

class Person {
  String name;
  int age;
  Person(this.name, this.age);
  Person.nav(name): this(name, 12);
}

# 常量构造函数

如果该类实例化的对象是不变的,可以把这些对象定义为编译时常量 定义一个Point类,包含一个常量构造函数,注意其成员都是final类型,且构造函数用const修饰

class Point {
  final int x;
  const Point(this.x);
}

构造两个相同的编译时常量会产生一个唯一的标准的实例

void main(){
  var p1  = const Point(2);
  var p2  = const Point(2);
  print(p1);
  print(p2);
  print(identical(p1,p2));  // true 说明这俩是同一个实例
}
void main(){
  var p1  = Point(2);
  var p2  = const Point(2);
  print(p1);
  print(p2);
  print(identical(p1,p2));  // false  说明这俩不是同一个实例
}

获取对象类型 runtimeType 属性,可以在运行时获取对象的类型

var p = new Person();
print(p.runtimeType); 

# 工厂构造函数

当执行构造函数并不总是创建这个类的一个新实例时,则使用factory 关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。

void main(){
  var m = new Machine('test');
  m.produce('computer');
}

class Machine {
  final String name;
  static final Map<String , Machine> _cache = <String , Machine>{};
  factory Machine(String name) {
    if(_cache.containsKey(name)){
      return _cache[name];
    }else{
      final machine = Machine._internal(name);
      _cache[name] = machine;
      return machine;
    }
  }
  Machine._internal(this.name);
  void produce(String sample){
    print(sample);
  }
}

# 实例方法

对象的实例方法可以访问this和实例变量

void main(){
  var p = new Person('zhangsan');
  p.say();
}

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

# Getter 和 Setter

用于对象属性的读和写,使用get和set关键字实现Getter和Setter

void main(){
  var t = new T(1,2);
  print(t.z);  // 3
  t.z = 4;
  print(t.x);  // 2
}

class T {
  int x,y;
  T(this.x, this.y);
  int get z => x + y;
  set z(int value) => x = value - y;
}

# 抽象类

修饰符 abstract 来定义一个抽象类,抽象类不能被实例化,只有继承它的子类可以,通常用来定义接口,以及部分实现,下面是一个具有抽象方法的抽象类

void main(){
  Dog dog = new Dog();
  dog.eat();
}

abstract class Animal {
  // 抽象方法 
  void eat();
}
class Dog extends Animal {
  eat(){
    print('dog eat');
  }
}

没有方法体的方法我们称为抽象方法.如果子类继承抽象类必须得实现里面的抽象方法.抽象类主要是用来约束子类.下面的例子中,抽象类中的普通方法是不需要被重写的.

void main(){
  Dog dog = new Dog();
  dog.eat();
  dog.fn();
}

abstract class Animal {
  void eat();  // 抽象方法 
  fn(){  // 普通方法 
    print('la la la');
  }
}
class Dog extends Animal {
  eat(){
    print('dog eat');
  }
}

extends抽象类 和 implements的区别:

  • 如果要复用抽象类里面的方法,并且要用抽象方法约束子类的话我们就用extends继承抽象类
  • 如果只是把抽象类当作标准的话我们就用implements实现抽象类

一个类实现多个接口

class Dog implements Animal,Animal2 {
  ...
}

extends关键字创建子类, super关键字引用父类

class Test1 {
  fn(){
    print('fn');
  }
}

class Test2 extends Test1 {
  fn(){
    super.fn();
    print('test2-fn');
  }
}

# 枚举类型

枚举类型是一种特殊的类,用于表示数量固定的常量值 定义一个枚举类型

enum Animal { dog, cat, fish}

返回枚举值的索引

print(Animal.fish.index);

使用枚举的values常量,获取所有枚举值列表

List<Animal> animal = Animal.values;

# mixin混合

复用类代码的一种方式,复用的类在不同层级之间,不存在继承关系.通过创建一个继承自Object且没有构造函数的类来实现一个mixin. 用法: with后跟一个或多个mixin的名字

void main(){
  var s = new Student('zhangsan', 12);
  print(s.age);
  print(s.isBoy);
  s.fn();
}

mixin M {
  bool isBoy = true;
}
mixin M2 {
  void fn(){
    print('M2 - fn');
  }
}

class Person {
  String name;
  Person(this.name);
}

class Student extends Person with M,M2 {
  int age;
  Student(String name, int age):super(name){
    this.age = age;
  }
}

# 类静态变量和方法

它们不能被实例使用,因此也不能访问this

void main(){
  Person.fn2();
  print(Person.a);
}

class Person {
  static const a = 2;
  static fn2(){
    print('fn2');
  }
}

# call()方法

通过实现类的call()方法,让类像函数一样被调用

void main(){
  var t = new Test();
  print(t('aa','bb'));
}
class Test {
  call(String a, String b) => '$a$b';
}