美文网首页
Dart基础(六)-面向对象和类

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

作者: 苍眸之宝宝 | 来源:发表于2022-01-10 10:10 被阅读0次

    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);
      }
    }
    

    相关文章

      网友评论

          本文标题:Dart基础(六)-面向对象和类

          本文链接:https://www.haomeiwen.com/subject/jzmlqrtx.html