Dart基础(六)-面向对象和类

1.简介:

  Dart是面向对象的语言,具有面向对象的三个基本特征,即封装、继承和多态。

  • 封装(Encapsulation):把客观事物封装成抽象的类,类中屏蔽内部的实现细节,只提供部分的属性和方法供外界访问。
  • 继承(Inheritance):子类自动共享父类的属性和方法,这是类之间的一种关系。子类可以对父类进行重写可扩展。
  • 多态(Polymorphism):允许将子类的指针赋值给父类类型的指针,同一个父类函数在不同子类中的行为不同。

2.Dart面向对象独有的特征:

  • Dart中一切皆对象,每个对象都是一个类的实例,包括数值类型;
  • 除了Null类之外,所有的类都继承自Object
  • Dart类中可以基于implement实现接口,且可以实现多个接口;
  • Dart类中可以基于mixin继承,且可以实现多继承;
  • Dart可以用Extension实现在不更改类或创建子类的情况下对类进行扩展;
  • Dart子类可以重写父类方法;
  • Dart方法不可以重栽,但是通过函数的参数的可选达到了方法重写的效果。

3.类的创建和使用:

  类中包含实例成员(对象成员)和静态成员,实例成员包含实例变量和实例方法,静态成员包含静态变量可静态方法。对象(也叫实例)是由函数functions(也叫做方法methods)和数据data(也叫做实例变量instance variables、成员变量members variables、属性properties、字段fields)组成的;类的对象访问成员变量或者方法需要.语法,可选用?.语法。
  类中静态成员可以通过类名加Type.语法进行访问。

3.1实例变量(instance variables):

  实例变量需要注意的点:

  • 所有未初始化的实例变量的值都为null
  • 所有实例变量都生成隐式getter方法,包括非final实例变量和没有初始化式的late final实例变量也会生成隐式setter方法;
    声明实例变量的关键字:
关键字 例子 意义
Type int a 表明实例变量的类型,有构造函数,必须在构造函数中初始化,没有构造函数必须提供默认值;初始化赋值后,后续可以继续改变其值
? int? a 表明实例变量可以为null值,初始化时不是必传参数 ;初始化赋值后,后续可以继续改变其值
final final int a 表明实例变量只能初始化赋值一次;有构造函数,必须在构造函数中初始化,没有构造函数必须提供默认值;初始化赋值后,后续无法修改其值
late late int a 表明实例变量可以延时初始化,及可以在构造函数外进行初始化赋值

注意:有构造函数表示的是自定义的构造函数,不包括默认构造函数。

3.2构造函数(Constructors):

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载

构造函数需要注意的点:

  • 类中提供类名同名的默认无参构造函数,且默认调用超类中的无实参构造函数;
  • 类自定义构造函数后,覆盖默认构造函数;
  • 构造函数无法继承,默认构造函数除外;
  • 构造函数和其它实例方法中this代表当前创建的实例,且只在名称冲突时使用。否则,Dart风格会省略this;
  • 构造函数和其它实例函数中super代表当前实例的父类指针;
  • 命名构造函数,用Type.functionName()语法定义的构造函数;
  • 命名构造函数与返回当前类实例的静态函数等同;
  • :可以重定向构造函数或者父类构造函数。
3.3构造函数调用顺序:

  默认情况下,子类中的默认构造函数调用超类的默认构造函数。
  父类的构造函数在子类构造函数体的开头被调用;如果初始化列表Initializer list也被使用,它会在调用超类之前执行。综上所述,执行顺序如下::

  1. 初始化列表;
  2. 父类的无参数构造函数的;
  3. 子类的无参数构造函数.
      如果父类有自定义构造函数(后续都成为构造函数),子类构造函数则必须手动调用父类中的一个构造函数;在冒号:之后指定父类构造函数;其含义是在调用子类的该构造函数之前,先执行父类的构造函数。
class Duck extends Bird {
  // 子类构造函数重定向父类构造函数
  Duck(String name, String wing) : super(name, wing);
}

class Point {
  double x;
  late final double y;
  late double? z;
  final double a;

  Point(this.x, this.y, this.z, this.a);
  // 命名构造函数,重定向构造函数
  Point.zero() : this(0, 0, 0, 0);

  static Point zero2() {
    final zero = Point(0, 0, 0, 0);
    return zero;
  }
}
3.4初始化列表Initializer list

  子类构造函数除了调用超类构造函数外,还可以在构造函数体运行之前初始化实例变量;用逗号分隔初始化式。

// 在调用构造函数前初始化实例变量
Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');
}
}
3.5常量构造函数Constant constructors

  如果类生成的对象永远不会改变,那么可以将这些对象设置为编译时常量。为此,定义一个const构造函数,并确保所有实例变量都是final变量。

class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}
3.6工厂构造函数Factory constructors

  当实现不总是创建类的新实例的构造函数时,请使用factory关键字。例如,工厂构造函数可以从缓存返回一个实例,也可以返回一个子类型的实例。工厂构造函数的另一个用例是使用无法在初始化列表中处理的final修饰的变量;另一种解决方法是late final修饰变量。工厂方法中无法使用this关键字代表当前创建实例
  在下面的例子中,Logger工厂构造函数从缓存中返回对象,Logger. fromjson工厂构造函数从JSON对象中初始化final变量。

// 
class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

// 外部访问
var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);
3.7方法methods

  Dart中,方法一般是指对象方法(或称实例方法),即对象能够调用的函数;方法是一种特殊的函数,即只能被对象调用的函数。
  对象上的实例方法可以通过this(可省略)访问对象的属性和方法,可以通过ClassType.语法访问静态成员(包含静态变量、静态常量、静态函数等)。

class MethodPoint {
  static const String className = "MethodPoint";
  static String star = "123";

  double x = 0;
  double y = 0;

  MethodPoint(this.x, this.y);

  void show() {
    print(MethodPoint.className);
    print(MethodPoint.star);
  }

  double distanceTo(MethodPoint other) {
    var dx = x - other.x; // 等价于:var dx = this.x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
3.8操作符方法:

  操作符可以作为特殊名称的实例方法。Dart允许你用以下名称定义操作符方法:

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

  注意:您可能已经注意到一些操作符,比如!=,不在名称列表中。那是因为它们只是语法糖。例如,表达式e1 != e2是!(e1 == e2)的语法糖。

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
}

// 调用
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  print(v + w == Vector(4, 5));
  print(v - w == Vector(0, 1));
3.9获取getters和设置方法setters

  gettersetter是提供对对象属性的读写访问的特殊方法。每个实例变量都有一个隐式getter,如果合适的话还有一个setter。你可以通过实现getter和setter来创建额外的属性,使用getset关键字:

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

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

  // Define two calculated properties: right and bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

    // 调用
    var rect = Rectangle(3, 4, 20, 15);
    print(rect.left == 3);
    rect.right = 12;
    print(rect.left == -8);

4.重写实例成员Overriding members

  子类可以覆盖实例方法(包括操作符方法)、getter和setter。你可以使用@override注释来表示你有意重写一个成员。
  重写实例方法的几个原则:

  • 返回类型必须与被重写方法的返回类型相同(或其子类型);
  • 参数类型必须与被重写方法的参数类型相同(或其超类型);
  • 如果重写的方法接受n个位置参数,那么重写的方法也必须接受n个位置参数;
  • 泛型方法不能覆盖非泛型方法,非泛型方法也不能覆盖泛型方法;
  • 重写==等价方法,必须要重写ObjecthashCodegetter方法。
class Television {
  // ···
  set contrast(int value) {...}
}

class SmartTelevision extends Television {
  @override
  set contrast(num value) {...}
  // ···
}

5.noSuchMethod():

  当对象访问不存在的方法或实例变量时,你可以重写noSuchMethod()来检测或做出反应。
  未实现的方法一般情况下是不能调用的,以下是能够调用的情况:

  • 接收器具有静态类型dynamic。
  • 接收方有一个静态类型,它定义了未实现的方法(抽象就可以了),而接收方的动态类型有一个noSuchMethod()的实现,它与Object类中的实现不同。

有关更多信息,请参阅非正式的noSuchMethod转发规范

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: '
        '${invocation.memberName}');
  }
}

6.枚举类型Enumerated types

  枚举类型是一种特殊的类,用于表示固定数量的常量值。
枚举类型注意的点:

  • 声明枚举的用enum Type语法;
  • 枚举中的每个值都有一个indexgetter方法,它返回枚举声明中值的从0开始的索引位置;例如,第一个值的索引为0,第二个值的索引为1;
  • 可以通过Type.values获取所有枚举值的列表;
  • 可以用switch语句枚举所有的值,如果你没有处理枚举的所有值,你会得到一个警告。
    枚举的限制:
  • 枚举类型不可继承extends、混入mixins和实现implement
  • 枚举无法实例化。
// 定义枚举类型
enum Color { red, green, blue }

// 获取枚举类枚举值的索引值
print(Color.red.index == 0);
print(Color.green.index == 1);
print(Color.blue.index == 2);

// 获取枚举类的所有枚举值
List<Color> colors = Color.values;
print(colors[2] == Color.blue);

// switch判断枚举值
var aColor = Color.blue;
switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // Without this, you see a WARNING.
    print(aColor); // 'Color.blue'
}

7.类成员(静态成员):

  Dart中,静态成员static members也称为类成员,包含静态变量和静态方法(或类变量和类方法)。
  类成员只能通过Type.语法访问。类方法不操作实例,因此不能访问实例成员;但是类方法可以访问类变量;类方法可以作为编译时常量作为参数传递给常量构造函数。类变量在被使用之前不会被初始化。

class Person {
  static String className = 'Person';
  late final String name;
  late final int age;

  void show() {
    print(name);
  }

  static void showClassName() {
    print(className);
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容