zoukankan      html  css  js  c++  java
  • 【dart学习】-- Dart之类和对象

    一,概述

      (Class)是面向对象程序设计,实现信息封装的基础。类是一种用户定义的类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。

          Dart的类与其它语言都有很大的区别,比如在dart的类中可以有无数个构造函数,可以重写类中的操作符,有默认的构造函数,由于dart没有接口,所以dart的类也是接口,因此你可以将类作为接口来重新实现。

      Dart是一门使用类和单继承的面向对象语言所有的对象都是类的实例,并且所有的类都是Object的子类。

    二,类定义

    • 类的定义用class关键字
    • 如果未显式定义构造函数,会默认一个空的构造函数
    • 类首字母必须大写

    • 使用new关键字和构造函数来创建对象
    • class  Person { //未定义父类的时候,默认继承自Object
        num x;
        num y;
        num z;
      }
      
      void main(List<String> args){
          var person = new Person();//调用默认的构造函数
          person.x = 10;   //使用点(.)引用实例变量或方法
          person.y = 11;
          person?.z = 12; //如果p不为空,设置它的变量y的值为4
          print(person.x);
          print(person.y);
          print(person.z);
      }

      结果:

      10
      11
      12

    三, 实例变量

    • 声明实例变量时,所有未初始化的实例变量的值为null
    • 对象的成员包括函数和数据(分别是方法和实例变量)。使用点(.)引用实例变量或方法; 
    • 使用?.来确认前操作数不为空, 常用来替代. , 避免左边操作数为null引发异常
    • void main(){
          var point = new Point();
          point.x = 4;  //使用点(.)引用实例变量或方法
      point?.y = 5;//如果p不为空,设置它的变量y的值为5
      print(point.x); print(point.y);
      }
      class Point { 
      int x; // null
      int y; // null
      int z = 0; // 0
      }

    四,构造函数

    • 如果你没有声明构造函数,默认有构造函数,默认构造函数没有参数,调用父类的无参构造函数。子类不能继承父类的构造函数
      class Person {
         int x;
         int y;
      }
      
      void main(List<String> args){
           var person = new Person();
      }
    • 构造函数就是一个与类同名的函数,关键字 this 是指当前的,只有在命名冲突时有效,否则dart会忽略处理
    • void main(){
          var point = new Point(4, 5);
      }
      class Point {
          int x;
          int y;
       //自己定义的类名构造函数
        Point(int x, int y) {
      this.x = x;
         this.y = y;
      }
      }
    • 在Dart中构造函数的名称可以是类名 ClassName  或者类名和标识符 ClassName.identifier 。 其中构造函数名称是“ClassName”的函数叫“类名构造函数”;构造函数名称是“ClassName.identifier”的函数叫“命名构造函数”

      (1)类名构造函数 (ClassName)

      import 'dart:math';
      
      class Point {
        int y;
        int x;
      
        // 类名构造函数
        Point(num x, num y) {
          this.x = x;
          this.y = y;
        }
        // .....
      }

      在构造函数里初始化成员属性是很常见的事情,因此Dart开发了新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成

      class Point {
        num x, y;
        // 注意x,y的赋值会在构造函数执行之前完成.
        Point(this.x, this.y);
      }


      (2)命名构造函数(ClassName.identifie) 
        使用命名构造函数可以为类提供多个构造函数,按官方的说法就是提供额外的清晰度

      class Point {
        num x, y;
      
        Point(this.x, this.y);
      
        // 命名构造函数
        Point.origin() {
          x = 0;
          y = 0;
        }
      }

        调用命名构造函数

      main(List<String> args) {
        // 调用命名构造函数
        Point point1 = Point.origin();
      }

              在命名构造函数里也可以用新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成   

    • class Point {
        num x, y;

      // 注意x,y的赋值会在构造函数执行之前完成.
        Point(this.x, this.y);

    • (2)命名构造函数(ClassName.identifie) 

        使用命名构造函数可以为类提供多个构造函数,按官方的说法就是提供额外的清晰度

      class Point {
      num x, y;
      
      Point(this.x, this.y);
      
      // 命名构造函数
      Point.origin() {
      x = 0;
      y = 0;
      }
      }

        调用命名构造函数

      main(List<String> args) {
      // 调用命名构造函数
      Point point1 = Point.origin();
      }

        在命名构造函数里也可以用新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成

      class Point {
        num x, y;
        //类名构造函数
         Point(this.x, this.y);
         // 命名构造函数
         Point.origin(this.x,this.y);
      }
      void main(List<String> args){
        var point = new Point.Orgin(1,2);
        print(point.x);
        print(point.y);
      }

      (3)默认构造函数(前面我我们已经说了,我们放在这里再提一下,方便区分)
         如果类中没有声明构造函数,Dart会提供一个默认的构造函数。这个默认的构造函数会调用父类的默认构造函数,并且该构造函数是没有参数的。

      class Person {
         int x;
         int y;
      }
      
      void main(List<String> args){
           var person = new Person();
      }
    • Dart的第一个版本实例化对象需要new关键字,但在Dart 2之后就去掉了new关键字

      main(List<String> args) {
        // 调用类名构造函数
        Point point1 = Point(3,4);
        print(point1.x);
      }
    • 调用父类非默认的构造函数(类比下面的重定向理解记忆)

      在默认情况下,子类可以调用父类的未命名,无参数的构造函数即默认构造函数。父类的构造函数会在子类的构造函数之前开始调用,如果子类中存在需要初始化的成员属性,则可以先初始化子类成员属性,再调用父类的构造函数,执行过程如下

      1. 初始化子类成员属性
      2. 调用父类构造函数
      3. 子类构造函数

      如果父类中没有默认的构造函数,你必须手动调用父类的构造函数,在子类的构造函数体之前通过 : 指定调用父类构造函数,示例如下

      // Person类中没有一个无参数,未命名的构造函数
      class Person {
        String firstName;
        // 命名构造函数
        Person.fromJson(Map data) {
          print('in Person');
        }
      }
      
      class Employee extends Person {
        // 你必须调用父类的super.fromJson(data).
        Employee.fromJson(Map data) : super.fromJson(data) {
          print('in Employee');
        }
      }
      
      main() {
        var emp = new Employee.fromJson({});
      }
    • 重定向构造函数 (在这里 :有重新指向的含义)

      有时构造函数的唯一目的是重定向到同一类中的另一个构造函数重定向构造函数的主体为空构造函数调用出现在冒号( :)之后 。 大意就是在创建类时,我定义一个命名构造函数,但是这个构造函式的主体我不实现。直接通过:另外一个构造函数。实现对外界传入的参数接收并赋值给内部的变量。

      class Point {
        num x, y;
        //类名构造函数
        Point(this.x, this.y);
        // 命名构造函数
       Point.order(this.x,this.y);
       Point.origin(num a,num b):this.order(a,b);  //重定向构造函数, origin构造函数将外界的传值,指向给了order构造函数。
      } 
      void main(List<String> args){ 
         var point = new Point.origin(1,2); 
         print(point.x); 
         print(point.y); 
      }
    • 常量构造函数 
      如果你的类创建的对象从不改变,你可以创建一些编译时的常量对象。因此,定义一个const构造函数,且保证所有的对象变量都是final。
      class ImmutablePoint {
        static final ImmutablePoint origin = const ImmutablePoint(0, 0);
        final num x, y;
        const ImmutablePoint(this.x, this.y);
      }
    • 工厂构造函数

      在实现一个构造函数时使用factory关键字,该构造函数并不总是创建其类的新实例。例如,工厂构造函数可能会从缓存中返回实例,也可能会返回子类型的实例。

      class Logger {
        final String name;
        bool mute = false;
      
       // _cache是私有变量
       //_在名称前,表示该变量为私有
        static final Map<String, Logger> _cache = <String, Logger>{};
      
        factory Logger(String name) {
          if (_cache.containsKey(name)) {
            return _cache[name];
           } else {
             final logger = Logger._internal(name);
             _cache[name] = logger;
             return logger;
          }
        }
      
        Logger._internal(this.name);
      void log(String msg) {
          if (!mute) print(msg);
        }
      }
      注意:工厂构造者对this没有访问权限。

      像调用任何其他构造函数一样调用工厂构造函数:

      var logger = Logger('UI');
      logger.log('Button clicked');

    五,方法

          方法是为对象提供行为的函数。  

    • Getters和Setters 方法
       (1)Getters和setters是读取和修改对象的特定方法,每次调用对象的属性时,Dart都会隐式的调用一次getter方法,这允许你可以在修改或者读取对象属时做一些操作。
       (2)通过 get和 set 关键词重写对象的默认行为。
      class Rectangle {
        num left, top, width, height;
        //类名构造函数
        Rectangle(this.left, this.top, this.width, this.height);
        // 重写right属性(类比oc记忆,这里多了一个set和get的关键字)
        num get right => left + width;
        set right(num value) => left = value - width;
        num get bottom => top + height;
        set bottom(num value) => top = value - height;
      }
      
      void main() {
        var rect = Rectangle(3, 4, 20, 15);
        assert(rect.left == 3);
        rect.right = 12;
        assert(rect.left == -8);
      }
    • 实例方法
      在对象的实例方法有权限获取对象变量和this,在接下来的例子里distanceTo就是一个对象方法:

      import 'dart:math';
      class Point {
        num x, y;
        Point(this.x, this.y);
        num distanceTo(Point other) {
          var dx = x - other.x;
          var dy = y - other.y;
          return sqrt(dx * dx + dy * dy);
        }
      }
    • 抽象方法

      (1) 实例的getter和setter方法就是抽象的方法,定义一个接口,但将其实现留给其他类。
      (2)抽象方法使用分号 ; 而不是方法体
      (3)抽象方法只存在于抽象类中。

      abstract class Doer {
        //...定义实例变量和方法...
      
        //定义一个抽象方法
        void doSomething();
      }
      
      class EffectiveDoer extends Doer {
        void doSomething() {
         //...实现一个抽象方法...
         }
      }

    六,抽象类和接口

    • 抽象类
      使用 abstract 修改器可以定义一个抽象类。抽象类是不能被实例化的,但对于定义接口是非常有用的,如果你想实例化抽象类,你必须实现抽象类,才能被实例化

    • // 此对象爱你过是抽象类,因此不能被实例化
      abstract class AbstractContainer {
        // 定义构造函数、字段、方法...
      
        void updateChildren(); // 抽象方法
      }
    • 隐式的接口
      每个类都是都是隐式的接口,包括类的方法和属性。如果你想创建一个类A不继承B的实现,可以实现B的接口来创建类A。一个类允许通过implements 关键词可以实现多个接口

      // 每个类都是一个隐式的接口,所以Person类也是个接口,包括成员属性和方法.
      class Person {
        // 可在接口中实现, 但仅对这个库可见.
        final _name;
      
        // 构造函数不能够被接口实现
        Person(this._name);
      
        // 可在接口中实现.
        String greet(String who) => 'Hello, $who. I am $_name.';
      }
      
      // 实现Person接口.
      class Impostor implements Person {
        get _name => '';
      
        String greet(String who) => 'Hi $who. Do you know who I am?';
      }
      
      String greetBob(Person person) => person.greet('Bob');
      
      void main() {
        print(greetBob(Person('Kathy')));
        print(greetBob(Impostor()));
      }

      实现多个接口

      class Point implements Comparable, Location {...}

    七,类的继承

    • 使用extends创建子类,super引用父类,子类可以重写实例方法、getter和setter,使用@override注释重写,使用@proxy注释来忽略警告
      class Television {
          void turnOn() {
              _illuminateDisplay();
              _activateIrSensor();
          }
      }
      
      class SmartTelevision extends Television {
          void turnOn();
          _bootNetworkInterface();
          _initializeMemory();
          _upgradeApps();
      }
    • 重写成员

      可以使用 @override 关键字,子类可以重写实例的方法,getters和setters

      class SmartTelevision extends Television {
        @override
        void turnOn() {...}
        // ···
      }
    • 重写操作符

      你可以重写以下表中显示的运算符。例如,如果定义Vecor类,则可以定义+方法来添加两个vectors。
      注意:
        你可能已经注意到了!=不是可重写的运算符。表达式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);
      
        // Operator == and hashCode not shown. For details, see note below.
        // ···
      }
      
      void main() {
        final v = Vector(2, 3);
        final w = Vector(2, 2);
      
        assert(v + w == Vector(4, 5));
        assert(v - w == Vector(0, 1));
      }

      如果你需要重写 == 操作符,请参考操作符教程

    • noSuchMethod()
      当用户调用你定义的类中不存在的属性与方法时,可以做出一些响应,通过重写noSuchMethod()

      class A {
        @override
        void noSuchMethod(Invocation invocation) {
          print('You tried to use a non-existent member: ' +
              '${invocation.memberName}');
        }
      }

       

    八,类变量和类方法  

      使用static关键字实现类范围的变量和方法。  

    • 静态变量
      静态变量(类变量)对于类范围的状态和常量非常有用:
      静态变量在使用之前不会初始化。
      class Queue {
        static const initialCapacity = 16;
        // ···
      }
      
      void main() {
        assert(Queue.initialCapacity == 16);
      }
    • 静态方法

      静态方法(类方法)不能在实例操作,因此它没有访问this的权限。

      import 'dart:math';
      
      class Point {
        num x, y;
        Point(this.x, this.y);
        static num 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 distance = Point.distanceBetween(a, b);
       assert(2.8 < distance && distance < 2.9);
       print(distance);
      }

      注意:
       (1)为了常用或广泛使用的实用程序和功能,考虑使用顶层函数,而不是静态方法。

      import 'dart:math';
      
      class Point {
       num x, y;
       Point(this.x, this.y);
      }
      // 顶级函数
       num 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 distance = distanceBetween(a, b);
       assert(2.8 < distance && distance < 2.9);
       print(distance);
      }

      (2)可以使用静态方法作为编译时常量。例如,可以将静态方法作为参数传递给常量构造函数。

    九,访问控制

    • 默认类中的所有属性和方法是public的。在dart中,可以在属性和方法名前添加“_”使私有化。现在让我们使name属性私有化。
      main(List<String> args) {
        Dog d = new Dog('Duffy', 5);
        print(d.name);  //This will throw error
      }
       
      class Dog {
        String _name; //私有的变量
        int age; //公有变量
       //类名构造函数
        Dog(this.name, this.age);
       //setter && getter 方法 
        String get respectedName {
          return 'Mr.$name';
        }
      set respectedName(String newName) {
          name = newName;
        }
       //命名构造函数
        Dog.newBorn() {
          name = 'Doggy';
          age = 0;
        }
       //公有实例方法
        bark() {
          print('Bow Wow');
        }
       //私有的实例方法
        _hiddenMethod() {
          print('I can only be called internally!');
        }
      }

    十,枚举类型

      枚举类型,通常称为枚举,是一种特殊类型的类,用于表示固定数量的常量值。

    • 使用枚举
      使用enum关键词来声明一个枚举类型

      enum Color { red, green, blue }

      枚举中的每个值都有一个index索引,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1。

      assert(Color.red.index == 0);
      assert(Color.green.index == 1);
      assert(Color.blue.index == 2);

      若要获取枚举中所有值的列表,请使用枚举的values常量。

      List<Color> colors = Color.values;
      assert(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: // 没有default,将会报错
          print(aColor); // 'Color.blue'
      }

      枚举类型有以下限制:

      • 你不能子类化、混合或实现枚举。
      • 不能显式实例化枚举。

     十一,泛型

    • 泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。
    • 从字面的意思理解来看,泛型,泛就是模糊、暂不确定暂定的意思。可以这样理解,使用泛型就是,定义的一个类型,类型暂不确定,给使用给一个占位符给代替,在使用的时候可以给确定其定义的类型。
    • 泛型,基本上强类型语言都支持泛型,比如java,Oc,swift 所以Dart也不例外
    • dart全面支持泛型。假设你想在你定义的类中,想持有任意类型的数据。如下是怎样使用泛型定义这样的类。
      main(List<String> args) {
        DataHolder<String> dataHolder = new DataHolder('Some data');
        print(dataHolder.getData());
        dataHolder.setData('New Data');
        print(dataHolder.getData());
      }
       
      class DataHolder<T> {
        T data;
       
        DataHolder(this.data);
       
        getData() {
          return data;
        }
       
        setData(data) {
          this.data = data;
        }
      } 
  • 相关阅读:
    MS CRM 2011 C#中获取Web Resource
    MS CRM 2011 Form与Web Resource在JScript中的相互调用
    MS CRM 2011 JScript操作lookup control
    MS CRM 2011 导出Ribbon的定义
    MS CRM 2011 为64位Outlook安装Silverlight
    MS CRM 2011 JScript getValue 与 setValue方法
    MS CRM 2011 在JScript中同步和异步使用REST Endpoint
    C#实现全局快捷键(系统热键)响应(转)
    程序隐藏启动的C#实现经验
    WinForm程序启动时不显示主窗体
  • 原文地址:https://www.cnblogs.com/lxlx1798/p/11016951.html
Copyright © 2011-2022 走看看