MENU

Dart入门5:类与对象

December 21, 2024 • Dart

类与对象

简介

  • 类是通过 class 声明的代码段,包含属性和方法。

    • 属性:用来描述类的变量
    • 方法:类中的函数称为类的方法


  • 对象是类的实例化结果

    • var obj = new MyClass ( )


  • 类是抽象,对象是实例

    • class person {
        String name = "";
        void get_info() {
          print("this is $name");
        }
      }
      
      void main() {
        person p = new person();
        p.name = "zhangsan";
        p.get_info();
      }
      
      

 

 

构造函数

  • 起初始化类变量的作用,有多种构造函数
  • 默认构造函数:与类名相同,没有返回值,在实例化对象时默认被调用

    • class Point {
        num x = 0, y = 0;
        Point(x, y) {
          this.x = x;
          this.y = y * x;
          print("default constructor function is aclled.x:${this.x},y:${this.y}");
        }
          
        Point(this.x, this.y); //简化的写法
      }
      
      void main() {
        Point p = new Point(10, 20);
      }
      
      


  • 命名构造函数:在类中使用命名构造器 (类名.函数名)实现多个构造函数,可以提供额外的初始化方式

    • class Point {
        num x = 0, y = 0;
        Point.special_point() {
          this.x = 100;
          this.y = 100;
        }
      }
      
      void main() {
        Point p = new Point.special_point();  //调用命名构造函数
        print(p.x);
      }
      


  • 常量构造函数:如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量

    • 使用常量的对象可以提高flutter渲染性能

    • class const_Point {
        //变量使用final定义、
        final x, y;
      
        //常量构造函数使用const定义,并且不能有函数体
        const const_Point(this.x, this.y);
      }
      
      void main() {
        //必须使用const关键字才能实例化常量对象
        var p1 = const const_Point(1, 2);
      }
      


  • 工厂构造函数:通过 factory声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例

    • 工厂构造函数不能进行实例化操作,也不能使用this关键字

    • 程序每次实例化消耗的资源都很大,工厂构造函数只会实例化一次,其实与单例模式差不多

    • class Persion {
        String name;
      
        static Persion? instance;
      
        factory Persion([String name = "zhangsan"]) {
          if (instance == null) {
            //第一次实例化
            instance = new Persion.newself(name);
          }
      
          return instance!;
        }
      
        Persion.newself(this.name);
      }
      
      void main(List<String> args) {
        //p1,p2是相同的
        var p1 = new Persion("lisi");
        print(p1.name);
      
        var p2 = new Persion("zhangsan");
        print(p2.name);
      }
      
      
      

 

 

访问修饰

  • 没有 public、protected、private等访问修饰符关键字,默认都是public;

  • 如果属性或方法以 ’_‘(下划线)开头,则表示私有(即 private);

  • 只有把类单独抽离出去,私有属性和方法才生效,例如 一个文件为 persion.dart,并且import

    • class persion {
        String name = "zhangsan";
        num _money = 0; //声明私有属性
      
        persion(this.name);
      }
      
      void main(List<String> args) {
        var p1 = new persion("lisi");
        print(p1._money);  //仍然可以访问私有,因为ipersion类和main函数在用一个作用域
      }
      
      
      // 如此就可以生效,可以对外提供公共方法访问私有属性
      import 'lib/persion.dart';
      
      void main(List<String> args) {
        var p1 = new persion("lisi");
        print(p1._money); //编译报错,无法访问
      }
      
      

 

 

Getter与Setter

  • Getter (获取器)是通过 get 关键字修饰的方法

    • 函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)


  • Setter (修改器)是通过 set 关键字修饰的方法

    • 访问时,像设置属性一样给函数传参


  • 其实就相当于更明确的提供了一组get和set的接口



  • import 'dart:ffi';

    class circle {
    final double pi = 3.14;
    num r = 0;

    circle(this.r);

    void setr(var r) {

    this.r = r;

    }

    num area() {

    return this.pi * this.r * this.r;

    }

    //Getter
    num get area1 {

    return this.pi * this.r * this.r;

    }

    //Setter
    void set setr1(var r) {

    this.r = r;

    }
    }

    void main(List<String> args) {
    var c1 = new circle(10);
    print(c1.area()); //使用内部get方法
    print(c1.area1); //使用getter属性

    c1.setr(1); //使用内部set方法
    c1.setr1 = 100; //使用serter
    print(c1.area1);
    }

     

初始化列表

  • 作用:在构造函数中设置属性的默认值

  • 时机:在构造函数体执行之前执行

  • 语法:使用逗号分隔初始化表达式

  • 场景: 常用于设置 final 常量的值

  • 其实可以使用 [ ],可选参数来实现相同的效果

    • class rect {
        int height, weight;
        rect()
            : height = 10,
              weight = 20 {
          print("${this.height} -- ${this.weight}");
        }
      }
      
      void main(List<String> args) {
        var r1 = new rect();
      }
      
      

 

Static

  • static关键字用来指定静态成员

    • 通过 static 修饰的属性是静态属性
    • 通过 static 修饰的方法是静态方法


  • 静态成员可以通过类名称直接访问(不需要实例化)

    • 实例化是比较消耗资源的,声明静态成员,可以提高程序性能
    • 静态方法不能访问非静态成员, 非静态方法可以访问静态成员
    • 静态方法中不能使用 this 关键字
    • 不能使用 this 关键字,访问静态属性


  • 实例化对象不能直接访问静态成员


 

元数据

  • 元数据以 @开头,可以给代码标记一些额外的信息,相当于注释,对代码实际运行没有影响

  • 元数据可以用来库,类,构造器,函数,字段,参数或变量声明的前面

  • @ override (重写)

    • 某方法添加该注解后,表示重写了父类中的同名方法


  • @required (必填)

    • 可以通过 @ required 来注解 Dart 中的命名参数,用来指示它是必填参数


  • @ deprecated (弃用)

    • 若某类或某方法加上该注解之后,表示此方法或类不再建议使用

 

继承

  • 根据类的先后顺序,可以将类分成父类和子类

  • 子类通过 extends 关键字 继承 父类,并且只能单继承

    • 继承后,子类可以使用父类中,可见的内容 (属性或方法)


  • 子类中,可以通过 @override 元数据来标记“覆写"方法

    • “覆写”方法:子类中与父类中同名的方法,会调用子类的


  • 子类中,可以通过 super 关键字来引用父类中,可见的内容,属性为public的

    • 属性
    • 方法 (普通构造函数,命名构造函数,以及其他方法)

 

抽象类

  • 抽象类是用 abstract关键字修饰的类

  • 抽象类的作用是充当普通类的模板,约定一些必要的属性和方法,充当模板以及接口

  • 抽象方法是指没有方法体的方法

    • 抽象类中一般都有抽象方法,也可以没有抽象方法
    • 普通类中,不能有抽象方法


  • 抽象类不能被实例化(不能被 new)



  • 抽象类可以被普通类继承 (extends)

    • 如果普通类继承抽象类,必须实现抽象类中所有的抽象方法


  • 抽象类还可以充当接口被实现 (implements)

    • 如果把抽象类当做接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法。


  • abstract class phone {
    var cpu;
    var screen;

    void processer(); //抽象方法没有函数体实现
    void camear();
    }

    class xiaomi extends phone {

    //普通类继承,必须按约束实现所有抽象方法
    @override
    void processer() {

    print(&quot;this is xiaomi xiaolong 888 cpu&quot;);

    }

    @override
    void camear() {

    print(&quot;this is xiaomi 1e camera&quot;);

    }
    }


 

接口

  • 接口在 Dart 中就是一个类 (只是用法不同)

    • 接口可以是任意类,但一般使用抽象类做接口


  • 一个类可以实现 (implements)多个接口,多个接口用逗号分隔

    • class MyClass implements Interface1, Interface2 { ... }


  • 接口可以看成一个个小零件,类实现接口就相当于组装零件

    • 普通类实现接口后,必须重写接口中所有的属性和方法


  • // 硬件接口
    abstract class hdware {
    String ipf = "";
    String chip = "";

    void hd_comp(String ipf, String chip);
    }

    // 软件接口
    abstract class software {
    String ver = "";
    String size = "";

    void soft_comp(String ver, String size);
    }

    // 通过普通类实现接口,实现所有抽象属性和方法,相当于把所有的组件拼装起来
    class phone implements hdware, software {
    @override
    String ipf = "";
    String chip = "";
    String ver = "";
    String size = "";

    phone(this.chip, this.ipf, this.ver, this.size);

    @override
    void hd_comp(String ipf, String chip) {

    print(&quot;this hd comp is ipf $ipf, chip $chip&quot;);

    }

    @override
    void soft_comp(String ver, String size) {

    print(&quot;this soft comp is ver $ver,  size $size&quot;);

    }
    }


 

混入

  • 混入(Mixin)是一段公共代码。混入有两种声明方式:

    • 将类当作混入 mixin class MixinA { ..}

      • mixin class既可以当作普通类使用,也可以当作mixin使用


    • 使用 mixin 关键字声明 mixin Mixin B { ..}




  • 混入(Mixin)可以提高代码复用的效率,解决了只能单继承的问题,普通类可以通过 with 来使用混入

    • class MyClass with MixinA, Mixin B { .. }


  • 使用多个混入时,后引入的混入会覆盖之前混入中的重复的内容

    • MixinA 和 MixinB 中都有 hello() 方法, MyClass 会使用 Mixin B 中的


  • //混入的声明方法1
    mixin class mixa {
    String name = "mixA,class";

    void printA() {

    print(&quot;A&quot;);

    }
    }

    //混入的声明方法2
    mixin mixb {
    String name = "mixB,mixin";

    void printB() {

    print(&quot;B&quot;);

    }
    }

    //使用混入
    class myclass with mixa, mixb {}

    class child extends mixa {}

    void main(List<String> args) {
    var c = myclass();
    print(c.name);

    //同时使用A和B的代码内容
    c.printA();
    c.printB();

    var chi = new child();
    chi.printA();
    }


 

泛型

  • 在函数、类、接口中指定宽泛数据类型的语法,作用在传入参数未知且多变时,对应的返回相应类型的数据,减少代码重复

  • //泛型函数
    T getdata<T>(T val1,T val2......)
    {
      return val;
    }
    
    getdata<int>(10);  //则T变为int
    getdata<String>("hello");  //则T变为String
    
  • //泛型类
    class generic<T> {
      Set myset = new Set<T>();
      void add(T value) {
        this.myset.add(value);
      }
    
      void info() {
        print(myset);
      }
    }
    
    void main(List<String> args) {
      var g = new generic<int>();
      g.add(2);
      g.add("3"); //报错,g的数据类型只能是int
      g.info();
    }
    
  • //泛型接口
    
    //缓存接口
    abstract class cache<T> {
      getbykey(String key);
      void setbykey(String key, T val);
    }
    
    //文件缓存实现
    class filecache<T> implements cache<T> {
      @override
      getbykey(String key) {
        print("this is file cache");
        return null;
      }
    
      @override
      setbykey(String key, T val) {
        print("this is key $key,val $val");
      }
    }
    
    //内存缓存实现
    class memcache<T> implements cache<T> {
      @override
      getbykey(String key) {
        print("this is file cache");
        return null;
      }
    
      @override
      setbykey(String key, T val) {
        print("this is key $key,val $val");
      }
    }
    
    void main(List<String> args) {
      var fc = new filecache<int>();  //缓存int类型
      var fc2 = new filecache<Set>();  //缓存set类型
    
      fc.setbykey("1", 50);
      fc2.setbykey("key1", new Set());
    }
    
    
  • 泛型类型为T时,可以传入任何类型,我们可以限制其只能传入某些特定类型

  • //泛型限制
    class basic {}
    
    class myclass<T extends basic> {   //限制T为basic以及其子类
      info() {
        print("this is $T");
      }
    }
    
    class ext1 extends basic {}
    
    class otherclass {}
    
    void main(List<String> args) {
      var f1 = new myclass<basic>();
      f1.info(); //this is basic
    
      var f2 = new myclass<ext1>();
      f2.info(); //this is ext1
    
      var f3 = new myclass<int>();  //编译报错,因为限制了T只能是basic以及其子类,不能传入其他类,int不是basic的子类
      var f4 = new myclass<otherclass>(); 
    }
    
    

 

枚举

  • 枚举定义一组有意义的常量

  • 通过values获取所有枚举值列表

  • 通过index获取枚举值对应的序号

  • enum chip { armv7, armv8, aarch64, x86 }
    
    void main(List<String> args) {
      print(chip.values);
      print(chip.aarch64.index);
    
      //将其转换为列表
      List<chip> chiplist = chip.values; .
          
      //通过list的下标访问
      print(chiplist[2]); 
        
      //通过列表的foreach访问,foreach是匿名函数
      chiplist.forEach((val) { 
        print("this is list val $val");
      });
    }