Dart(三)--类

类是面向对象编程语言中最为重要的一类概念,Dart中的类也只支持一个父类,除了null外,所有类的超类都为Object

一、类

1.class 定义一个类

class关键字标识一个类的定义,在类内部可以定义其成员变量:

class P {
  String? name;// 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;
}
2.构造函数

Dart和Java一样,如果你没有定义构造函数,那么运行时会自动生成一个空的构造并调用父类的空构造,定义构造函数与类名相同,防止参数名与成员变量名重复,构造函数中可以通过this.成员变量名进行赋值:

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;

  P(String name, int age, int gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

Dart中以类名为方法名的构造函数只能有一个,如果你想传递不同数量的参数,可以使用可选参数:

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;

  P({String? name, int? age, int? gender}) {
    this.name = name;
    this.age = age;
    this.gender = gender ?? 0;
  }
}
2.1 终值初始化

Dart提供了一种简便的构造函数语法糖:类名(this.成员变量1,this.成员变量2...);,其中也是支持可选参数的语法,将上面的构造函数改写后:

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;

  // 对于非空成员变量,可选参数中赋值一个默认值
  P({this.name, this.age, this.gender = 0});

  // P({String? name, int? age, int? gender}) {
  //   this.name = name;
  //   this.age = age;
  //   this.gender = gender ?? 0;
  // }

}
2.2 命名式构造函数

虽然只允许我们定义一个类名构造函数,但Dart提供了另外定义构造函数的方式,命名式构造函数:类名.方法名(),这样方法名不同,就可以作进一步的区分

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;

  P({this.name, this.age, this.gender = 0});

  // 定义命名式构造函数 name age必传 ,gender非必须
  P.gen1(String name, int age, {int? gender}) {
    this.name = name;
    this.age = age;
    this.gender = gender ?? 0;
  }

}

main() {
  P p = P.gen1("hi", 23);
}

命名式构造函数也支持终值语法糖:

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;

...
  
  P.gen2(this.name, this.age);

}
2.3 初始化列表

所有构造函数支持在后面跟上一个:进行初始化成员变量,每个成员变量之间使用,分隔,下面是将name和age在初始化列表中进行初始化的代码:

class P {
  String? name; // 可空对象不初始化,值默认为null
  int? age;
  int gender = 0;
    
  // name age必传 ,gender非必须
  P.gen1(String name, int age, {int? gender})
      : this.name = name,
        this.age = age {
    this.gender = gender ?? 0;
  }
}
3.子类
3.1 extends 继承父类

子类通过extends关键字指定父类,子类的构造函数默认继承父类的匿名无参数构造方法:

class Man extends P {
  int? height;
}
3.2 子类构造

子类定义构造函数时可以通过: + super.父类构造函数手动指定一个父类构造,该父类构造可以时常规构造函数,也可以时命名式构造函数,子类构造函数中的参数可以直接传递到父类构造中:

// 子类
class Man extends P {
  int? height;

  Man(String name, int age, int height) : super.gen2(name, age) {
    this.height = height;
  }
}

除了this支持终值语法糖,super也支持,我们将子类构造简化后:

// 子类
class Man extends P {
  int? height;

  Man(super.name, super.age, this.height) : super.gen2();
}
4.其他构造函数

上面我们使用了常用的构造函数,Dart中还有一些用的较少的

4.1 重定向构造函数

重定向构造函数和继承父构造函数类似,有时候类中的构造函数仅用于调用类中其它的构造函数,没有函数体,只需在该构造函数后使用:指定需要重定向到的其它构造函数 (使用 this 而非类名)

class P {
  String? name;
  int? age;
  int gender = 0;

  P(this.name, this.age, this.gender);

  P.base(String name) : this(name, null, 0);
}
4.2 常量构造函数

如果类生成的对象都是不变的,可以在生成这些对象时就将其变为编译时常量。你可以在类的构造函数前加上 const 关键字并确保所有实例变量均为 final 来实现该功能。

class P {
  final String? name;
  final int? age;
  final int gender;

  const P(this.name, this.age, this.gender);
}
4.3 工厂构造函数

使用factory修饰构造函数,标识该构造函数为工厂模式,仅仅只是一个标识,具体功能还需要手动实现,如:从缓存中获取一个实例,在工厂构造函数中无法访问 this

class P {
  final int id;
  String? name;
  int? age;
  int? gender;

  static final Map<int, P> _cache = {};

  factory P.cache(int id) {
    return _cache.putIfAbsent(id, () => P._internal(id));
  }

  P._internal(this.id, {this.name, this.age, this.gender});
}

main() {
  P p1 = P.cache(0);
  P p2 = P.cache(0);

  print(identical(p1, p2));
}

运行结果:

5.成员函数

成员函数为在类内部定义的方法,除了常规函数的功能外,成员函数还支持下面的内容

5.1 operator 操作符重载

我们可以通过operator去自定义自己类的+-*等操作符,支持的操作符如下:

<+``
>/^[]
<=~/&[]=
>=*<<~
-%>>==

下面例子重载了减法:

class Point {
  double x, y;

  Point(this.x, this.y);

  // - 法重载
  operator -(Point p2) => Point(x - p2.x, y - p2.y);

  
  String toString() {
    return 'Point{x: $x, y: $y}';
  }
}

main() {
  Point p1 = Point(4, 5);
  Point p2 = Point(2, 3);
  p1 -= p2;

  print(p1);
}

运行结果:

5.2 Getter 和 Setter

Getter 和 Setter 是一对用来读写对象属性的特殊方法,每一个成员变量都有一个隐式的 Getter 方法,如果为非 final 属性的话还会有一个 Setter 方法,你可以使用 getset 关键字为额外的属性添加 Getter 和 Setter 方法

class Rectangle {
  double left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  double get right => left + width;
  set right(double value) => left = value - width;

  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

main(){
  Rectangle rectangle = Rectangle(10, 30, 100, 200);
  
  print("right:${rectangle.right} bottom:${rectangle.bottom}");
}

运行结果:

二、抽象类与接口

1. 抽象类

使用abstract修饰类,让该类成为抽象类,抽象类中方法可以不实现,交由子类实现,称为抽象方法

abstract class P {
  void call();// 抽象方法不需要实现
}

一般情况下,抽象类不可以实例化,使用factory定义工厂构造函数后可被实例化

abstract class P {
  P();

  factory P.getDefault() => DP();

  void call();
}

class DP extends P {
  
  void call() {
    print("hi");
  }
}

main() {
  P.getDefault().call();
}
2.接口

Dart只能继承一个父类,但在Dart中,每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的成员变量的Getter 和 Setter函数与类成员函数,使用implements可以实现多个接口

// P 作为接口
class P {
  String name;

  P(this.name);

  call() => "hi";
}

class B implements P {
  // 实现name的Getter 和 Setter
  
  String get name => "B"; 
  
  set name(String value) => name = value;

  // call方法
  
  call() {
    print("hello");
  }
}

三、枚举类

枚举类使用enum关键字定义,你可以在 Switch 语句中直接使用枚举

1.简单枚举

没有成员变量与函数的枚举

enum Color { red, green, blue }
2.增强枚举

除了简单枚举外,Dart还支持含有成员变量与函数的枚举,用法和类类似,其成员变量必须为final,构造函数使用const 修饰。

enum Vehicle implements Comparable<Vehicle> {
  car(tires: 4, passengers: 5, carbonPerKilometer: 400),
  bus(tires: 6, passengers: 50, carbonPerKilometer: 800),
  bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);

  const Vehicle({
    required this.tires,
    required this.passengers,
    required this.carbonPerKilometer,
  });

  final int tires;
  final int passengers;
  final int carbonPerKilometer;

  int get carbonFootprint => (carbonPerKilometer / passengers).round();

  
  int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}

四、Mixin模板类

Mixin 是一种在多重继承中复用某个类中代码的方法模式。一个类可以使用with指定一个或多个模板类,他类似于将Mixin类中的代码直接复制一份到该类中

1.mixin 定义模板类

使用mixin 关键字,定义一个模板类

// 定义mixin
mixin Logger {
  bool isDebug = true;

  i(String info) {
    if (isDebug) {
      print(info);
    }
  }
}

// 使用mixin
class Hi with Logger {
  Hi() {
    i("constructor Hi()");
  }
}

main(){
  Hi();
}

运行结果:

2.on 指定可以使用的类

定义Mixin类时,可以通过on指定哪些类可以使用该模板

class Debugger {}

// 定义mixin 只有Debugger的子类才能使用
mixin Logger on Debugger {
  bool isDebug = true;

  i(String info) {
    if (isDebug) {
      print(info);
    }
  }
}

// 使用mixin
class Hi extends Debugger with Logger {
  Hi() {
    i("constructor Hi()");
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值