类 class

什么是类 和对象

类就是一类,归类,把我们身边的东西抽象出来,变成一个类型,比如全世界的人抽象成一个人类,然后具体的一个人,例如Bob,Tom这些具体的一个人,就是对象。

就像一个“蓝图”或“模板”,定义了对象的结构和行为。

还是那人类,来说,人这个类,就是一个模板,人类有什么?名字、有手脚等器官,能说话,能思考。所以,对于名字和年龄等等这些是属于人类的属性特征。对于说话,思考等属于人类的动作行为。所以,类就是把现实中的东西,提取出一个“类”出来,然后有了这个类,就可以去创建出一个个的对象,例如Jack,Jim等等的这些人。

上面说的类的属性(姓名,年龄),和类的行为(说话,思考),我们可以通过变量和函数来表示。

它描述了对象应该有哪些 属性(变量)方法(函数)

类是一个摸具,它不是一个具体的东西,就好比你做面包用的摸具,通过这个模具制造出一个个实体的面包。

人类的编程发展历史,一开始是没有“类”的概念的,例如C语言,没有类,只有函数,变量,各种的语句等等,这时候叫面向过程。随着软件的发展,功能越来越丰富,人们发现用买那些过程的编程语言来开发的时候,会出现很多重复的代码,而且代码管理混乱,不够层次感,没有调理。后来有人想出来,可以把编程的逻辑抽象成一个个的对象,和现实一样。就好比一个公司,一开始什么工作都老板亲历亲为,后来,老板招人了,他要做统计,就找财务,他要招人,就找人力。这样各司其职,是不是就条例清晰很多了。

对象 是类的一个 实例(Instance)

当你用 new 或构造函数创建时,类就会生成一个对象。

对象是实际存在的,可以存储数据、调用方法。

类与对象的关系

  • 类是 设计图,对象是 实际建造出来的房子
  • 一个类可以创建多个对象,每个对象有自己独立的数据。
  • 所有对象共享类中定义的行为(方法)。

下面回到我们的dart中

我们将会学习如下的东西

Dart 是面向对象语言,所有对象都是类的实例。除了 Null,所有类都继承自 Object

Dart 基于类 + Mixin 实现继承

单继承:每个类只有一个父类,但 Mixin 可复用多套类逻辑

扩展方法:无需改类 / 新建子类即可给类新增功能

类修饰符:控制其他库能否继承 / 实现该类

下面我们开始学习Dart中的类

类成员

类的成员,包括方法与变量,所谓成员,就是类里面的组成成员。

使用 . 访问实例成员;?. 空安全访问,避免空指针异常

下面是一个完整的例子,里面有创建类,通过类实例化对象,通过对象名调用成员

// 创建类 相当于创建一个模具 
class Point {
  double x, y;
  Point(this.x, this.y);

  // 实例方法:计算两点距离
  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return dx * dx + dy * dy;
  }
}

void main() {
  // 通过Point类来创建对象p 对象才可以进行各种操作
  var p = Point(2, 2);
  
  // 读取实例变量(隐式 getter)
  print(p.y); //2.0

  // 调用实例方法
  double dist = p.distanceTo(Point(4, 4));
  print(dist); // 8.0

  // ?. 空安全访问
  Point? nullablePoint;
  var a = nullablePoint?.y; // p为null,a直接赋值null,不抛异常
  print(a); // null
}Code language: Dart (dart)

构造函数(Constructors)

普通构造函数

Dart和其他类C语言有点不应用,它还有一个命名构造函数,其他类C语言都是使用和类名同名的构造函数,Dart可以用和类名不同的名称来表示构造函数。

格式:类名() / 类名.标识符()

new 关键字可省略(Dart2+后才有的)

class Point {
  double x, y; //两个变量 属于类成员 用来存储类的属性 
  
  // 默认构造 用来初始化上面两个变量 x y
  Point(this.x, this.y);
  
  // 命名构造 Point.fromJson 表示这个构造函数是通过json来初始化x y的
  Point.fromJson(Map<String, double> json)
      : x = json['x']!,
        y = json['y']!;
		
}

void main() {
  var p1 = Point(2, 2);//调用默认构造函数 传入2 2 然后初始化里面的x y  p1的x是2 y是2
  
  var p2 = Point.fromJson({'x': 1, 'y': 2}); //通过fromJson命名构造函数 传入json
  
  // new 可写可不写,效果完全一致
  var p3 = new Point(3, 3); //同样使用默认构造函数
  
  print("p1:(${p1.x},${p1.y}), p2:(${p2.x},${p2.y}), p3:(${p3.x},${p3.y})");
  //输出:p1:(2.0,2.0), p2:(1.0,2.0), p3:(3.0,3.0)
}Code language: Dart (dart)

常量构造 const

构造函数加 const,创建编译期常量;相同参数常量会复用同一个实例

class ImmutablePoint {
  final double x, y;
  // 常量构造:所有成员必须final,也就是构造函数有const修饰,则成员x, y 需要final
  const ImmutablePoint(this.x, this.y);
}

void main() {
  // 常量实例
  var a = const ImmutablePoint(1, 1); //这里创建实例的时候,也需要const 
  var b = const ImmutablePoint(1, 1);
  print(identical(a, b)); // true 同一个对象
  
  var m = const ImmutablePoint(1, 1); 
  var n = const ImmutablePoint(2, 1);
  print(identical(m, n)); // false x的值不同 对象就不同了

  // 无const,普通对象,不共享实例
  var c = ImmutablePoint(1, 1);
  print(identical(a, c)); //false

  // const上下文可省略内层const
  const pointMap = {
    'p1': [ImmutablePoint(0, 0)],
    'p2': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)]
  };
  
  print(pointMap); //{p1: [Instance of 'ImmutablePoint'], p2: [Instance of 'ImmutablePoint', Instance of 'ImmutablePoint']}

  print(identical(p1, p2)); // false
  print(identical(p2, p3)); // false
  print(identical(p1, p3)); // false
}Code language: Dart (dart)
常量构造入参完全一致才会复用同一个实例,任意参数不同则是两个独立对象

获取对象运行时类型 runtimeType

通过 对象.runtimeType 获取运行时类型;优先用类型判断运算符 is,而非 runtimeType 判等

class Point {
  double x, y;
  Point(this.x, this.y);

  // 实例方法:计算两点距离
  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return dx * dx + dy * dy;
  }
}

void main() {
  var p = Point(2, 3);
  print("p 的类型:${p.runtimeType}"); // Point

  // 推荐写法(稳定)
  if (p is Point) {
    print("p 是 Point 类型");
  }
  
  // 不推荐生产环境使用
  if (p.runtimeType == Point) {
	print("p 是 Point 类型 ");
  }
}Code language: Dart (dart)

实例变量 Instance Variables

  1. 可空类型未初始化:默认 null;非空必须初始化
  2. 所有实例变量自动生成 getter;非 final / 无初始化 late final 自动生成 setter
  3. 普通实例变量初始化表达式不能访问 this;late 变量初始化可访问 this
  4. final 实例变量只能赋值一次:声明时 / 构造参数 / 初始化列表赋值

隐式 get/set

class Point {
  double? x; // 初始null,自动get+set
  double? y;
  double z = 0; // 初始0
}

void main() {
  var point = Point();
  point.x = 4; // 调用隐式setter
  assert(point.x == 4); // 调用隐式getter
  assert(point.y == null);
  print(point.z); // 0
}
Code language: JavaScript (javascript)

初始化表达式 this 访问限制

double globalX = 1.5;
class Point {
  // 合法:访问外部全局变量,不依赖this
  double? x = globalX;

  // 报错:普通初始化不能用this
  // double? y = this.x;

  // 合法:late 初始化可访问this
  late double? z = this.x;

  // 合法:构造参数语法糖 this.x 不是初始化表达式
  Point(this.x, this.y);
  double? y;
}

void main() {
  var p = Point(null, 5);
  print(p.z); // null
}
Code language: JavaScript (javascript)

final 实例变量三种初始化方式

class ProfileMark {
  // 方式1:声明直接初始化
  final DateTime start = DateTime.now();
  final String name;

  // 方式2:构造参数赋值
  ProfileMark(this.name);

  // 方式3:构造初始化列表赋值 前面的命名构造函数 参数没传入 默认给''
  ProfileMark.unnamed() : name = '';
}

void main() {
  var m1 = ProfileMark("user1");
  var m2 = ProfileMark.unnamed();
  print(m1.name);
  print(m2.name);
}
Code language: PHP (php)

隐式接口 Implicit interfaces

Dart 每个类自带隐式接口:包含自身所有实例成员。

  • implements:实现接口,必须重写接口全部方法
  • extends:继承,复用父类实现
  • 私有变量 _xxx 不会暴露到接口

1 实现单个接口

class Person {
  final String _name;
  Person(this._name);
  // 接口公开方法
  String greet(String who) => "Hello $who, I'm $_name";
}

// 实现Person接口,必须重写所有接口成员
class Impostor implements Person {
  String get _name => "";
  @override
  String greet(String who) => "Hi $who, guess who?";
}

String greetBob(Person p) => p.greet("Bob");

void main() {
  print(greetBob(Person("Kathy")));
  print(greetBob(Impostor()));
}
Code language: PHP (php)

子类重写父类接口的时候,用@override来标注,和java类似,子类继承父类的时候用implements 关键字

2 实现多个接口

//抽象类
abstract class Comparable {
  int compareTo(Object other);
}

//抽象类
abstract class Location {
  double get x;
  double get y;
}

//实现两个接口
class Point implements Comparable, Location {

  @override
  double x, y;

  Point(this.x, this.y);

  @override
  int compareTo(Object other) {
    if (other is Point) {
      return (x + y).compareTo(other.x + other.y);
    }
    return -1;
  }
}

void main() {
  var p = Point(2, 3);
  print(p.x);
  print(p.compareTo(Point(1, 1)));
}
Code language: PHP (php)

@override
double x, y;

注解,表示:这两个属性是实现父接口 Location 里定义的抽象 getter

类静态变量 & 静态方法 static

1. 静态变量 static(类变量,全局唯一)

  • 属于类,不属于实例;延迟初始化,使用时才创建
  • 常量静态变量推荐小驼峰命名
class Queue {
  static const initialCapacity = 16; //静态变量
  static int count = 0; //静态变量
  Queue() {
    count++;
  }
}

void main() {
  assert(Queue.initialCapacity == 16); //调用 就会创建 默认是16
  Queue(); //让count+1
  Queue();
  print(Queue.count); // 2
}
Code language: JavaScript (javascript)

2. 静态方法 static(类方法)

  • this,不能访问实例成员;仅能访问静态变量
  • 直接 类名.方法() 调用
  • 可作为编译期常量传入常量构造函数
import 'dart:math';

class Point {
  double x, y;
  Point(this.x, this.y);

  // 静态方法:计算两点距离
  static double distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var dist = Point.distanceBetween(a, b);
  print(dist); // ~2.828
}Code language: JavaScript (javascript)

静态方法属于类的,所以需要类名来调用,传入两个Point的实例。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注