Dart基础>Dart语法(下)

待兔
• 阅读 1195

前文链接:

内容:

  • 函数(方法)
    函数定义及各类函数;函数参数;闭包
  • 面向对象
    定义;构造函数;成员(变量与函数)
    继承与多态;抽象类;接口;枚举类
    Mixins;操作符
  • 泛型
    定义;用法;限制泛型类型
  • 库和可见性
  • 异常
  • 元数据

五、函数(方法)

1、说明:

  • Dart 是一个真正的面向对象语言,方法也是对象并且具有一种类型, Function

  • 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。也可以把 Dart 类的实例当做方法来调用。 详情请参考 Callable classes

  • 方法都有返回值,当没有指定返回值的时候,函数返回null,即最后默认执行一句return null,可以省略不写。

2、函数定义及各类函数:

  • 基本形式:

    返回值 方法名(参数1, 参数2, ...) {
      方法体
      return 返回值;
    } 

    示例:

    int sum(int a, int b) {
      return a + b;
    } 
  • 省略模式:
    1、定义方法的返回值类型参数 都可以省略

    sum(a, b) {
      return a + b;
    }
    
    //sumResult = 9
    print("sumResult = ${sum(3, 6)}"); 

    说明:建议明确方法(函数)的输入类型和返回值类型,既便于修改,也方便阅读。重要的是,如果不写方法参数输入类型,则在调用的时候,调用者可能无法明确参数类型(经测试,在编译阶段并未有相应的参数类型的检查提示),这就可能导致Unhandled exception之类的错误。

  • 箭头函数:
    1、语法:=> expr
    2、是 { return expr; } 形式的缩写。=> 形式 有时候也称之为 胖箭头 语法。
    注:只适用于一个表达式的方法。
    3、示例:

    int sum(int a, int b) => a + b; 
  • 匿名函数:
    1、没有名字的函数,称之为匿名函数,有时候也被称为lambda 或者 closure 闭包。 2、你可以把匿名函数赋值给一个变量, 然后你可以通过这个变量使用这个函数。
    3、匿名函数和命名函数看起来类似,在括号之间可以定义一些参数,参数使用逗号分割,也可以是可选参数,大括号中的代码为函数体。

    • 定义:([[Type] param1[, …]]) { codeBlock; };

    • 示例:``` Function sum = (int a, int b)

       {
         return a + b;
       };

      //sum = 9 print("sum = ${sum(3, 6)}");

      var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums']; //其中forEach接收一个函数 list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i)); ```

  • 入口函数:
    1、每个应用都需要有个顶级的 main() 入口方法才能执行。 main() 方法的返回值为 void 并且有个可选的 List<String> 参数。

    void main(List<String> args) {
       print(arguments);
    } 

    通过命令行可以将参数打印出来:

    Dart基础>Dart语法(下)

    入口函数调用

说明:其中使用`dart` 命令调用,参数用**空格**隔开
  • 函数别名
    1、在 Dart 语言中,方法也是对象。
    2、使用 typedef, 或者 function-type alias 来为方法类型命名, 然后可以使用命名的方法。
    3、当把方法类型赋值给一个变量的时候,typedef 保留类型信息。

    • 看下面一个简单的例子:
    typedef int compare(int a, int b);
    
    int sort(int a, int b) {
      return a - b;
    }
    
    void main(List<String> args) {
      //(int, int) => int
      print(compare);
      //Closure: (int, int) => int from Function 'sort': static.
      print(sort);
      //true
      print(sort is Function);
      //true
      print(sort is compare);
    } 

    说明:通过is 操作符可以判断两个对象是否相等。

    • 看下面的例子:
      下面的代码没有使用 typedef:
    class SortedCollection {
      Function compare;
    
      SortedCollection(int f(Object a, Object b)) {
        compare = f;
      }
    }
    
     // Initial, broken implementation.
     int sort(Object a, Object b) => 0;
    
    main() {
      SortedCollection coll = new SortedCollection(sort);
    
      // 我们只知道 compare 是一个 Function 类型,
      // 但是不知道具体是何种 Function 类型?
      assert(coll.compare is Function);
    } 

    说明:当把 f 赋值给 compare 的时候, 类型信息丢失了。 f 的类型是 (Object, Object) → int (这里 → 代表返回值类型), 当然该类型是一个 Function。 如果我们使用显式的名字并保留类型信息, 开发者和工具可以使用 这些信息:

    typedef int Compare(Object a, Object b);
    
    class SortedCollection {
      Compare compare;
    
      SortedCollection(this.compare);
    }
    
     // Initial, broken implementation.
     int sort(Object a, Object b) => 0;
    
    main() {
      SortedCollection coll = new SortedCollection(sort);
      assert(coll.compare is Function);
      assert(coll.compare is Compare);
    } 
    • 注意: 目前,typedefs 只能使用在 function 类型上,但是将来 可能会有变化。

3、函数参数

  • 静态作用域
    1、Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。
    2、基本上大括号里面定义的变量就 只能在大括号里面访问,和 Java 作用域 类似。

    var topLevel = true;
    
    main() {
        var insideMain = true;
    
        myFunction() {
            var insideFunction = true;
    
            nestedFunction() {
                var insideNestedFunction = true;
    
                print(topLevel);  //true
                print(insideMain);  //true
                print(insideFunction);  //true
                print(insideNestedFunction);  //true
            }
    
            //undefined name 'insideNestedFunction'
            //print(insideNestedFunction);
    
            nestedFunction();
        }
    
        //undefined name 'insideFunction'
        //print(insideFunction);
    
        myFunction();
    } 
  • 可选参数
    1.可选参数包括:可选命名参数可选位置参数
    2.但是这两种参数不能同时当做可选参数。
    3.可选参数的方法,在调用的时候,是可选的,可传入可不传入
    4.可选参数只能放到方法参数的末尾,不能放到必需参数的前面

    • 可选命名参数:
      定义方法时,可选命名参数需要将可选的参数放到{}
      调用方法时,方法中的参数可以通过这种形式 paramName: value 来指定命名参数
     main() {
           //name:张三;isMan:null;age:null
           printArgs("张三");
           //name:李四;isMan:false;age:null
           printArgs("李四", isMan: false);
           //name:王五;isMan:null;age:88
           printArgs("王五", age: 88);
           //name:赵六;isMan:true;age:55
           printArgs("赵六", isMan: true, age: 55);
       }
    
       void printArgs(String name, {bool isMan, int age}) {
           print("name:$name;isMan:$isMan;age:$age");
       } 
    • 可选位置参数:
      定义方法时,可选位置参数需要将可选的参数放到[]
      调用方法时,方法中的参数根据位置来指定命名参数
     main() {
           //name:张三;isMan:null;age:null
           printArg("张三");
           //name:李四;isMan:true;age:null
           printArg("李四", true);
           //无法调用
           //printArg("王五", 29);
           //name:赵六;isMan:true;age:29
           printArg("赵六", true, 29);
       }
    
       void printArg(String name, [bool isMan, int age]) {
           print("name:$name;isMan:$isMan;age:$age");
       } 
  • 默认参数值
    1、在定义方法的时候,可以使用 = 来定义可选参数的默认值。
    2、默认值只能是编译时常量。
    3、如果没有提供默认值,则默认值为 null。

    void main() {
      //name:张三;age:39;isMain:true
      printArgs("张三", 39);
      //name:Lili;age:33;isMain:false
      printArgs("Lili", 33, isMan: false);
    }
    
    void printArgs(String name, int age, { bool isMan=true }) {
      print("name:$name;age:$age;isMain:$isMan");
    } 
  • 测试函数是否相等
    下面是测试顶级方法、静态函数和实例函数 相等的示例:

     foo() {}               // 一个顶级方法
    
       class A {
           static void bar() {} // 一个静态方法
           void baz() {}        // 一个实例方法
       }
    
       main() {
           var x;
    
           // 比较 顶级方法.
           x = foo;
           print(foo == x);
    
           // 比较静态方法
           x = A.bar;
           print(A.bar == x);
    
           // 比较实例方法
           var v = new A(); // A的第一个实例#1
           var w = new A(); // A的第二个实例#2
           var y = w;
           x = w.baz;
    
           // 这些闭包引用同一个实例(#2),因此它们是相等的
           print(y.baz == x);
    
           // 这些闭包引用不同的实例,因此它们不等
           print(v.baz != w.baz);
       } 

4、闭包

  • 特性:
    1、一个 闭包 是一个方法对象。
    2、闭包定义在其他方法的内部,一般通过return将其作为返回值返回。
    3、不管闭包对象(方法返回的)在何处被调用,该对象都可以访问其(即闭包所在的方法)作用域内的变量,并持有其状态。

  • 示例:

    void test() {
        Function add = makeAdder(1);
        int result = add(2);
        //result = 3
        print("result = $result");
    }
    
    /**
     * 定义返回方法的函数
     */
    Function makeAdder(num outerNum) {
        return (num innerNum)=> outerNum + innerNum;
    } 
  • 一段有意思的代码:

     void main() {
           var callbacks = [];
    
           for (var i = 0; i < 3; i++) {
               // 在列表 callbacks 中添加一个函数对象,这个函数会记住 for 循环中当前 i 的值。
               callbacks.add(() => print('Save $i'));
           }
           //[Closure: () => void, Closure: () => void, Closure: () => void]
           print(callbacks);
           callbacks.forEach((c) => c()); // 分别输出 Save 0 1 2
       } 

    说明:

    • for 循环中,向callbacks 中加入的是一个匿名函数(此处定义的匿名函数的作用是打印局部变量i),此函数持有循环中的变量i(一般的,i作为局部变量,循环结束就被回收了),即闭包的特性(持有外部方法中变量的状态)。
    • forEach 函数接收的是一个函数对象(匿名函数:(c) => c())作为参数,每次循环进行调用此函数,即为callbacks 数组中的函数对象。

六、面向对象

1、定义

  • Dart 是一个面向对象编程语言,同时支持基于 mixin 的继承机制。

  • 每个对象都是一个类的实例,所有的类都继承于 Object

  • 基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他多个类继承中重复使用。即一个类可以继承自多个父类。

  • 使用关键字calss 声明一个类

  • 使用关键字new 创建一个对象,new 可以省略。

  • 对象的成员包括方法和数据 (函数 和 实例变量)。

  • 示例:``` class Person { }

    void main() {

       Person p = new Person();
    
       //省略new关键字
       Person pp = Person();

    }

2、构造函数

  • 定义:
    1、定义一个和类名字一样的方法就定义了一个构造函数。
    2、还可以带有其他可选的标识符,形如ClassName.identifier

     class Person {
           Person() {
               print("person===super");
           }
       }
    
       class Student extends Person {  //这里是继承父类,后面会总结
           Student() {
               print("student====this");
           }
       }
    
       void main() {
           Student s = Student();
    
           //打印结果:
           //person===super
           //student====this
       } 
  • 默认构造函数
    1、如果未显式定义构造函数,会默认一个空的构造函数。
    2、默认构造函数没有参数,并且会调用超类的没有参数的构造函数。

     class Person {
           Person() {
               print("person===super");
           }
       }
    
       class Student extends Person {  //这里是继承父类,后面会总结
       }
    
       void main() {
           Student s = Student();
    
           //打印结果:
           //person===super
       } 
  • 自定义构造函数
    1、如果存在自定义构造函数,则默认构造函数无效,即只能存在一个构造函数(这也验证了函数不能重载的特性)。
    2、其中this 关键字指当前的实例。

     class Person {
    
           String name;
           int age;
    
           Person(String name, int age) {
               this.name = name;
               this.age = age;
           }
    
           //报错:The default constructor is already defined.
           Person() {
               print("person===super");
           }
       } 
  • 语法糖
    1、由于把构造函数参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作:

     class Person {
    
           String name;
           int age;
    
           /*
           //常规写法
           Person(String name, int age) {
               this.name = name;
               this.age = age;
           }*/
    
           //语法糖写法
           Person(this.name, this.age);
    
       } 
  • 命名构造函数

    • 说明:
      1、使用命名构造函数可以为一个类实现多个构造函数
      2、使用命名构造函数来更清晰的表明你的意图
      3、构造函数不能继承,所以超类的命名构造函数也不会被继承。

    • 实现方式:类名.方法。其中方法名称可以自定义

    • 示例:``` class Person {

      String name;
      int age;
      
      //语法糖写法
      Person(this.name, this.age);
      
      //fromName名称可以随便起名
      Person.fromName(String name) {
          this.name = name;
      }

      //withAge名称可以随便起名

      Person.withAge(int age) {
          this.age = age;
      }
      
      void printArgs() {
          print("name:$name;age:$age");
      }

      }

      void main() {

      Person p = Person("张三", 18);
      //name:张三;age:18
      p.printArgs();
      
      Person pp = Person.fromName("李四");
      //name:李四;age:null
      pp.printArgs();
      
      Person ppp = Person.withAge(33);
      //name:null;age:33
      ppp.printArgs();

      } ```

  • 常量构造函数

    • 说明:
      1、使用常量构造函数可以创建编译时常量,即类是不可变状态。
      2、使用const 声明构造方法,并且所有变量都为final。 3、要使用常量构造函数只需要用 const 替代 new 即可,也可以省略const。 4、两个一样的编译时常量其实是 同一个对象(通过identical可进行对比)。

    • 示例:``` class Person {

      final String name;
      
      final int age;
      
      const Person(this.name, this.age);
      
      void printArgs() {
          print("name:$name;age:$age");
      }

      }

      void main() {

      const p = const Person("张三", 33);
      const pp = const Person("张三", 33);
      
      //true
      print(identical(p, pp));  //比较两个对象是否相等

      } ```

  • 工厂构造函数

    • 说明:
      1、如果一个构造函数并不总是返回一个新的对象,则可以将其定义为工厂构造函数。
      2、工厂构造函数,类似于设计模式中的工厂模式。
      3、在构造方法前添加关键字factory 实现一个工厂构造方法。
      4、在工厂构造方法中可以返回对象。

    • 注意:工厂构造函数无法访问 this

    • 示例:
      下面代码演示工厂构造函数 如何从缓存中返回对象。``` class Logger { final String name; bool mute = false;

      // _cache 是个库私有变量,在变量名前加_即为私有成员变量 static final Map<String, Logger> _cache =

        <String, Logger>{};

      //添加 factory 定义为工厂构造函数 factory Logger(String name) {

      //如果缓存中有name,则取出返回,若不存在则添加并返回。
      if (_cache.containsKey(name)) {
        return _cache[name];
      } else {
        final logger = new Logger._internal(name);
        _cache[name] = logger;
        return logger;
      }

      }

      Logger._internal(this.name);

      void log(String msg) {

      if (!mute) {
        print(msg);
      }

      } } 调用: var logger = new Logger('UI'); logger.log('Button clicked'); ```

  • 重定向构造函数

    • 说明:
      1、有时候一个构造函数会调动类中的其他构造函数,则可以通过重定向构造函数。
      2、一个重定向构造函数是没有代码的,在构造函数声明后,使用 : 调用其他构造函数。

    • 示例:``` class Person {

      String name;
      int age;
      
      //语法糖写法
      Person(this.name, this.age);
      
      Person.formAge(int age): this("张三", age);
      
      //此种写法没有给任何变量赋值,在调用后,name和age都为null
      Person.initParams(String name, int age);
      
      void printArgs() {
          print("name:$name;age:$age");
      }

      } ```

  • 初始化列表

    • 说明:
      1、在构造函数体执行之前除了可以调用超类构造函数之外,还可以初始化实例参数。即初始化列表会在构造方法体执行前执行。
      2、使用: 设置初始化表达式,使用 , 分隔初始化表达式。
      3、初始化列表常用于设置final 变量的值。
      官网警告: 初始化表达式等号右边的部分不能访问 this。(本人验证,似乎并非如此。)

    • 示例:
      官网示例:``` class Point { num x; num y;

      Point(this.x, this.y);

      // Initializer list sets instance variables before // the constructor body runs. Point.fromJson(Map jsonMap)

        : x = jsonMap['x'],
          y = jsonMap['y'] {
      print('In Point.fromJson(): ($x, $y)');

      } } 本地测试: /**

      • person

      • / class Person { String name; int age; bool isMan;

        //初始化列表,加上了this Person(name, age):

              this.name = name,
              this.age = age;

        //初始化列表,加上了this Person.withMap(Map map): this.isMan = map["isMan"] {

          this.name = map["name"];
          this.age = map["age"];

        }

        void printArgs() {

          print("name:$name;age:$age");

        }

      }

      /**

      • main

      • / import 'person.dart'; void main() { Person p = new Person("张三", 33);

        //name:张三;age:33;isMan:null p.printArgs();

        Person pp = Person.withMap({

          "name": "李四",
          "age": 23,
          "isMan": true});

        //name:李四;age:23;isMan:true pp.printArgs(); } ```注:本人在测试的时候,在初始化列表上的变量加上了this 关键字,编译并未报错,也可以正常执行输出结果。
        这个地方不知是否是我的姿势有误,还是说确实可以如此使用,希望有知道的盆友可以解答我的困惑,非常感谢。
        不过,一切以官方为准,最好不要加上this

    • 设置final 变量:``` import 'dart:math';

      class Point { final num x; final num y; final num distanceFromOrigin;

      Point(x, y)

        : x = x,
          y = y,
          distanceFromOrigin = sqrt(x * x + y * y);

      }

      main() { var p = new Point(2, 3); print(p.distanceFromOrigin); }

3、成员(变量与函数)

  • 说明:
    1、对象的成员包括方法和数据 (函数 和 实例变量)。
    2、当你调用一个函数的时候,你是在一个对象上 调用:函数需要访问对象的方法 和数据。
    3、所有没有初始化的变量值都是 null。
    4、函数不能被重载,可以被子类覆写。

  • 调用:

    • 使用点. 来引用对象的变量或者方法。
    • 使用 ?. 来替代 . 可以避免当左边对象为 null 时候 抛出异常:
     class Person {
           //定义实例变量
           String name;
           int age;
    
           //定义实例函数         
           void printArgs() {
              print("name:${name};age:${age}");
           }
    
            //演示方法不能被重载
           //编译错误:The name 'printArgs' is already defined.
           void printArgs(int age) {
              print("name:${name};age:${age}");
           }
       }
    
       void main() {
           Person p = new Person();
           p.name = "张三";
           p.age = 33;
    
           //name:张三;age:33
           p.printArgs();
    
           Person pp;
           pp?.name = "李四";
    
           //报错:Unhandled exception:
           //NoSuchMethodError: The setter 'age=' was called on null.
           //Receiver: null
           //Tried calling: age=23
           pp.age = 23;
       } 
  • 实例变量

    • 说明:
      1、所有没有初始化的变量值都是 null。
      2、每个实例变量都会自动生成一个 getter 方法(隐含的)。
      3、非final 变量会自动生成一个 setter 方法(隐含的)。
      4、如果你在实例变量定义的时候初始化该变量(不是 在构造函数或者其他方法中初始化),该值是在实例创建的时候 初始化的,也就是在构造函数和初始化参数列 表执行之前。

    • 示例:``` class Point { num x; num y; }

      main() { var point = new Point(); point.x = 4; // 调用x,是用了setter方法 assert(point.x == 4); // 调用x,是用了getter方法 assert(point.y == null); // 变量默认是null } ```

  • 实例函数

    • 说明
      1、函数是类中定义的方法,是类对象的行为。
      2、对象的实例函数可以访问 this

    • 示例``` import 'dart:math';

      class Point { num x; num 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);

      } } ```

  • 计算属性-Getters And Setters

    • 说明
      1、Getters 和 setters 是用来设置和访问对象属性的特殊函数。 2、每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 3、你可以通过实行 gettersetter 来创建新的属性, 使用 getset 关键字定义 gettersetter。 4、计算属性的值是通过计算而来,本身不存储值。
      5、计算属性赋值,其实是通过计算转换到其他实例变量。
      6、在开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。

    • 官网注意(本人此处还未搞明白什么意思)
      1、像 (++) 这种操作符不管是否定义 getter 都会正确的执行。 为了避免其他副作用, 操作符只调用 getter 一次,然后把其值保存到一个临时变量中。

    • 示例

      class Rectangle {
        num left;
        num top;
        num width;
        num height;
      
        Rectangle(this.left, this.top, this.width, this.height);
      
        // 定义两个计算属性:right 和 bottom.
        num get right             => left + width;
            set right(num value)  => left = value - width;
        num get bottom            => top + height;
            set bottom(num value) => top = value - height;
      }
      
      main() {
        var rect = new Rectangle(3, 4, 20, 15);
        assert(rect.left == 3);
        rect.right = 12;
        assert(rect.left == -8);
      } 
  • 类变量和类函数(静态成员)

    • 说明
      1、使用static 关键字来实现类级别的变量和函数
      2、静态成员不能访问非静态成员(this调用),非静态成员可以访问静态成员。
      [静态函数不再类实例上执行, 所以无法访问 this]。 3、类中的常量需要使用static const 声明。

      4、静态变量对于类级别的状态是非常有用的。
      5、静态变量在第一次使用的时候才被初始化。
      6、静态函数还可以当做编译时常量使用。例如,你可以把静态函数当做常量构造函数的参数来使用。
      注意:对于通用的或者经常使用的静态函数,考虑使用顶级方法而不是静态函数。

    • 示例

      class Page {
      
          int x;
          static int currentPage = 1;
      
          static void upPage() {
              currentPage++;
              print("up--> currentPage = $currentPage");
          }
      
          static void downPage() {
              //报错:Invalid reference to 'this' expression.
              //print(this.x);    //不能访问非静态成员
              currentPage--;
              print("down--> currentPage = $currentPage");
          }
      }
      
      void main() {
          //1
          print(Page.currentPage);
      
          //up--> currentPage = 2
          Page.upPage();
      
          //down--> currentPage = 1
          Page.downPage();
      } 
  • 抽象函数
    详见抽象类

  • 对象call方法(可调用的类)

    • 说明:
      1、如果 Dart 类实现了 call() 函数,则对象可以当做方法来调用。
      2、只要方法名为call,无论有无参数、有无返回值,都是可以的

    • 示例:``` class Person {

      String name;
      int age;
      
      void call() {
          print("name:$name;age:$age");
      }

      } class Student {

      String name;
      int age;
      
      void call(String name, int age) {
          print("name:$name;age:$age");
      }

      } class Worker {

      String name;
      int age;
      
      String call(String name, int age) {
          return "name:$name;age:$age";
      }

      }

      void main() {

      Person person = Person();
      //name:null;age:null
      person();  //因为实现了call方法,直接调用即可
      
      Student student = Student();
      //name:学生;age:15
      student("学生", 15);
      
      Worker worker = new Worker();
      String info = worker("工人", 33);
      //work==>name:工人;age:33
      print("work==>$info");

      }

更多相关信息: Emulating Functions in Dart(在 Dart 中模拟方法)

4、继承与多态

  • 定义
    1、使用关键字extends 继承一个类
    2、子类会继承父类可见的属性和方法(可以用@override 注解来表明为覆写方法),不会继承构造方法
    3、子类能够覆写父类的方法、gettersetter
    4、Dart具有单继承、多态性
    5、Dart的多态性可以让子类实例指向父类的变量。

    • 示例``` class Person {

      String name;
      int age;
      
      void work() {
          print("person--->working...");
      }
      
      bool get isAdult => age > 18;
      
      void printArgs() {
          print("person==>name:$name;age:$age;isAdult:$isAdult");
      }

      }

      class Student extends Person {

      Student() {
      
      }
      
      Student.initParams(String name, int age) {
          this.name = name;
          this.age = age;
      }
      
      void study() {
          print("student--->studying...");
      }
        @override
        bool get isAdult => age > 15;

        @override
        void printArgs() {
            //super.printArgs();
            print("student==>name:$name;age:$age;isAdult:$isAdult");
      }
    }

    void main() {
        Student student = Student();
        student.name = "张三";
        student.age = 16;

        //person--->working...
        student.work();
        //student==>name:张三;age:16;isAdult:true
        student.printArgs();

        //多态调用
        Person p = Student.initParams("李四", 17);
        //student==>name:李四;age:17;isAdult:true
        p.printArgs();
        if (p is Student) {
           //student--->studying...
           p.study();
        }
    } 
    ```
  • 继承中的构造函数
    1、子类的构造方法默认会调用父类的无名无参构造函数。
    2、如果父类没有无名无参的构造函数,则需要显式调用父类构造函数。
    3、在构造方法参数后使用: 显式调用父类构造函数。
    4、如果有初始化列表,初始化列表要放在父类构造函数之前。

    注:子类不能使用重定向构造函数(无方法体的:设置变量的构造函数)进行自定义函数。_本人认为_:因为初始化参数要提前于父类构造函数,这个时候还不能使用this(待验证)。

    • 示例
     class Person {
           String name;
           int age;
    
           /*
           #1
           Person() {
               print("person===");
           }
           */
    
           Person(this.name);
    
           Person.initParams(this.name, this.age);
    
       }
    
       class Student extends Person {
    
           //第一种
           Student(String name) : super(name);
    
           //第二种
           Student.initParams(String name, int age) : super.initParams(name, age);
    
           // 无法创建
           // Student.initParams(String name, int age):this.name = name;
       }
    
       void main() {
           //#1
           //打印:person===
           //Student student = Student();
       } 

    初始化列表:

     class Person {
          String name;
          int age;
    
          Person(this.name);
    
          Person.initParams(this.name, this.age);
    
      }
    
      class Student extends Person {
    
          final bool isMan;
    
          Student.withParams(String name, int age, bool man) :
                  isMan = man,
                  super.initParams(name, age);
      }

    ```

  • 构造方法执行顺序
    1、父类的构造函数在子类构造函数的方法体开始执行的位置调用。
    2、如果有初始化列表,初始化列表会在父类构造函数之前执行。

    • 示例:``` class Person {

      String name;
      int age;
      
      Person(this.name);
      
      Person.initParams(this.name, this.age) {
          print("Person.initParams====");
      }

      }

      class Student extends Person {

      final bool isMan;
      
      Student.withParams(String name, int age, bool man) :
              isMan = getMan(man),
              super.initParams(name, age) {
          print("Student.withParams====");
      }
      
      static bool getMan(man) {
          print("Student===man:$man");
          return man;
      }

      }

      void main() {

      Student student = Student.withParams("学生", 17, true);
      
      /**
       * 打印如下:
          Student===man:true
          Person.initParams====
          Student.withParams====
       */

      } ```

  • 扩展:
    查看所有超类Object,其中有一些成员的前面用关键字external 来修饰,它的作用是根据不同平台(Dart是跨平台的语言)语言而有不同的具体实现。

     /**
       * Returns a string representation of this object.
       */
      external String toString(); 

5、抽象类

  • 定义
    1、抽象类是一个不能被实例化的类。
    2、使用 abstract 修饰符定义一个 _抽象类_。

  • 说明:
    1、抽象类通常用来定义接口, 以及部分实现。
    2、如果你希望你的抽象类是可实例化的,则定义一个 工厂构造函数。 3、抽象类通常具有 抽象函数。 4、抽象类不能直接被实例化。
    5、抽象类可以没有抽象方法。
    6、有抽象方法的类一定要声明为抽象类。

    • 示例

      abstract class Person {
        void run();
      
        void printArgs() {
          print("person===");
        }
      }
      
      class Student extends Person {
      
        @override
        void run() {
          print("student run...");
        }
      }
      
      void main() {
        Person p = new Student();
        //student run...
        p.run();
      } 
    • 注意:
      官网说明:下面的类(示例)不是抽象的,但是定义了一个抽象函数,这样的类是可以被实例化的。
      但经过测试,确实是有警告,运行也会报错,不太明白这里所说的可以被实例化是什么意思。

```
 // This class is declared abstract and thus
   // can't be instantiated.
   abstract class AbstractContainer {
     // ...Define constructors, fields, methods...

     void updateChildren(); // Abstract method.
   }


   class SpecializedContainer extends AbstractContainer {
     // ...Define more constructors, fields, methods...

     void updateChildren() {
       // ...Implement updateChildren()...
     }

     // Abstract method causes a warning but
     // doesn't prevent instantiation.
     void doSomething();
   } 
```
  • 抽象函数

    • 抽象方法不用abstract 修饰,没有方法体实现。
    • 实例函数、 getter、和 setter 函数可以为抽象函数。
    • 抽象函数是只定义函数接口但是没有实现的函数,由子类来实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。
    • 调用一个没实现的抽象函数会导致运行时异常。

6、接口

  • 定义
    一个类通过使用关键字implements 来实现一个或者多个接口。

  • 说明
    1、类和接口是统一的,类就是接口。 2、一个类实现了某个接口,就要实现此接口的每个成员。
    3、如果是复用已有类的接口,使用继承(extends)。
    4、如果只是使用已有类的外在行为,使用接口(implements)。
    5、每个类都隐式的定义了一个包含所有实例成员的接口

  • 示例:``` class Person {

       String name;
       int get age => 18;
    
       void run() {
           print("run...");
       }

    }

    class Student implements Person {

     @override
     String name;
    
     @override
     // TODO: implement age
     int get age => 15;
    
     @override
     void run() {
         print("student run...");
     }

    }

    void main() {

       Student student = Student();
       student.run();

    } ```

  • 建议:
    1、将抽象类作为接口使用,让子类来实现(因为类即接口,所以可以通过关键字implements来实现)。
    示例:``` abstract class Person {

       void run();

    }

    class Student implements Person {

     String name;
    
     int get age => 15;
    
     @override
     void run() {
         print("student run...");
     }

    }

    void main() {

       Student student = Student();
       //student run...
       student.run();

    }

7、枚举类

  • 定义
    使用关键字enum 来定义一个枚举类型。

  • 说明
    1、枚举类型通常称之为 enumerations 或者 enums,是一种特殊的类,用来表现一个固定数目的常量。
    2、枚举是一种有穷序列集的数据类型。
    3、常用于代替常量,控制语句等。

  • 特性
    1、枚举中的index 属性从0开始,依次累加。
    2、枚举中的values 属性可以列举出所有的枚举值。x
    3、无法显示的初始化一个枚举类型,即不能指定原始值(给枚举常量赋值)。
    4、无法继承枚举类型、无法使用 mixin、无法实现一个枚举类型。

  • 示例:

     enum Color {
           red,
           green,
           blue,
    
           //报错,不能指定原始值
           //white = "#FFFFFF",
       }
    
       void main() {
    
           var color = Color.red;
    
           switch(color) {
               case Color.red:
                   print("红色");
                   break;
               case Color.blue:
                   print("蓝色");
                   break;
               case Color.green:
                   print("绿色");
                   break;
           }
           //红色
    
           //index==> 0
           print("index==> ${color.index}");
    
           List<Color> values = Color.values;
           //[Color.red, Color.green, Color.blue]
           print(values);
       } 

8、Mixins

  • 定义
    使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。

  • 说明
    1、Mixins 类似于多继承,实在多类继承中重用一个类代码的方式。
    2、作为Mixin的类不能显示声明构造函数,不能调用 super 。(由于沿着继承链传递构造函数参数的需要,该约束能避免出现新的连锁问题。)
    3、作为Mixin的类只能继承自Object

  • 参考链接:
    Dart学习笔记(33):Mixin混合模式

  • 示例

    • 继承示例:

      class A {
          void a() {
              print("A.a...");
          }
      }
      
      class B {
      
          void a() {
              print("B.a...");
          }
          void b() {
              print("B.b...");
          }
      }
      
      class C {
      
          void a() {
              print("C.a...");
          }
          void b() {
              print("C.b...");
          }
      
          void c() {
              print("C.c...");
          }
      }
      
      class D extends A with B, C {
      
      }
      
      void main() {
          D d = D();
          //C.a...
          d.a();
      } 

      说明:
      这里打印了C.a... 是和继承的顺序有关的,如果将D 类继承BC 的顺序互换,则会调用B 中的方法,打印结果为B.a...

    • 组合示例:

      abstract class Engine {
          void work();
      }
      
      class OilEngine implements Engine {
        @override
        void work() {
          print("Work with oil...");
        }
      
      }
      
      class ElectricEngine implements Engine {
      
        @override
        void work() {
          print("Work with electric...");
        }
      
      }
      
      class Tyre {
          String name;
      
          void run() {}
      
      }
      
      class Car = Tyre with ElectricEngine;
      
      class Bus = Tyre with OilEngine; 

      说明:
      这种方式一般可以实现模块的组装,将自己需要的模块进行组合,实现不同的功能。

  • 官网说明:
    从 Dart 1.13 开始, 这两个限制在 Dart VM 上 没有那么严格了:

    • Mixins 可以继承其他类,不再限制为继承 Object
    • Mixins 可以调用 super()

9、操作符

  • 对象操作符

    • ?.:条件成员访问
      as:类型转换
      is:判断是指定类型
      is!:判断非指定类型
      ..:级联操作,即可连续调用对象成员,因为会返回当前对象。

    • 示例``` class Person {

      String name;
      int age;
      
      void work() {
        print("person--->name:$name;age:$age");
      }

      }

      void main() {

      var p;
      p = "";
      p = new Person();
      //不会报错
      p?.age = 43;
      
      p.name = "张三";
      p.age = 43;
      //person--->name:张三;age:43
      (p as Person).work();
      
      if (p is Person) {
          //person--->name:张三;age:43
          p.work();
      }
      
      Person person = Person();
      person..name = "Tom"
            ..age = 33
            ..work(); //person--->name:Tom;age:33

      }

  • 操作符覆写

    • 说明:
      1、操作符的覆写需要在类中定义。
      2、如果覆写了 == ,则还应该覆写对象的 hashCodegetter 函数。 关于 覆写 ==hashCode 的示例请参考 实现 map 的 keys

    • 可覆写操作符

      < +
      > / ^ []=
      <= ~/ & ~
      >= * << ==
      % >>
    • 格式:``` class Xxx {

      返回类型 operator 操作符(参数1, 参数2, 参数....) {
          方法体
          return 返回值;
      }

      } ```

    • 示例:``` class Person {

      int age;
      
      Person(this.age);
      
      bool operator >(Person p) {
          return this.age > p.age;
      }
      
      int operator [](String ageParam) {
          if("age"==ageParam) {
              return this.age;
          } else {
              return 0;
          }
      }
      
      /**
       * 以下两个覆写方法,可以通过右键选择'Generate...'
       * 然后点击选择'== and hashCode'直接生成
       */
      @override
      bool operator ==(Object other) =>
          identical(this, other) ||
              other is Person &&
                  runtimeType == other.runtimeType &&
                  age == other.age;
      
      @override
      int get hashCode => age.hashCode;

      }

      void main() {

      Person p1 = Person(22);
      Person p2 = Person(33);
      //p1>p2? ==> false
      print("p1>p2? ==> ${p1>p2}");
      
      //p1.age==> 22
      print("p1.age==> ${p1["age"]}");

      }

七、泛型

1、定义

  • 方式
    1、使用 <…> 来声明泛型
    2、通常情况下,使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。
    3、List 是一个 泛型 (或者 参数化) 类型,定义为List<E>

  • 使用泛型的原因
    1、在 Dart 中类型是可选的,可以通过泛型来限定类型。
    2、使用泛型可以有效地减少重复的代码。 泛型可以在多种类型之间定义同一个实现,同时还可以继续使用检查模式和静态分析工具提供的代码分析功能。
    3、如果你需要更加安全的类型检查,则可以使用 参数化定义。

2、用法

  • 类的泛型

    • 说明:
      在调用构造函数的时候, 在类名字后面使用尖括号(<...>)来指定 泛型类型。

    • 示例:``` class Cache {

      T value;
      
      T get() {
          return value;
      }
      
      void put(T value) {
          this.value = value;
      }

      }

      void main() {

      Cache<String> cacheStr = Cache();
      cacheStr.put("张三");
      //张三
      print(cacheStr.get());
      
      Cache<int> cacheNum = Cache();
      cacheNum.put(333);
      //333
      print(cacheNum.get());

      } ```

  • 函数的泛型

    • 说明:
      在函数上使用泛型,可以在如下地方使用类型参数(具体见示例):
      1、函数的返回值类型 (T)。
      2、参数的类型 (T value).
      3、局部变量的类型 (T temp).

    注意: 版本说明: 在 Dart SDK 1.21. 开始可以使用泛型函数。

    • 示例:``` class Util {

      static T put<T>(T value) {
          T temp;
          if (value!=null) {
              temp = value;
          }
          print("temp = $temp");
          return value;
      }

      }

      void main() {

      //张三
      String value = Util.put<String>("张三");
      //报错,提示类型错误
      Util.put<String>(1);

      }

3、限制泛型类型

  • 说明
    当需要对泛型的具体类型进行限定的时候,可以使用extends 关键字来限定泛型参数的具体类型。

  • 示例:``` // T must be SomeBaseClass or one of its descendants. class Foo {...}

    class Extender extends SomeBaseClass {...}

    void main() {

     // It's OK to use SomeBaseClass or any of its subclasses inside <>.
     var someBaseClassFoo = new Foo<SomeBaseClass>();
     var extenderFoo = new Foo<Extender>();
    
     // It's also OK to use no <> at all.
     var foo = new Foo();
    
     // Specifying any non-SomeBaseClass type results in a warning and, in
     // checked mode, a runtime error.
     // var objectFoo = new Foo<Object>();

    }

八、库和可见性

1、简介

  • Dart中的可见性以library 为单位
  • 默认情况下,每一个Dart文件就是一个库
  • 使用_ 表示库的私有性。
  • 使用import 关键字导入库

2、示例:

  • person.dart

    /**
     * peerson
     */
    class Person {
        String name;
        int age;
    
        Person(name, age):
                this.name = name,
                this.age = age;
    
        void printArgs() {
            print("name:$name;age:$age");
        }
    } 
  • main

    import 'person.dart';
    
    void main() {
        Person p = new Person("张三", 33);
    
        //name:张三;age:33
        p.printArgs();
    } 

Dart基础>Dart语法(下)

image.png

九、异常

1、简介

  • 代码中可以出现异常和捕获异常。

  • 异常表示一些未知的错误情况。

  • 如果异常没有捕获,则异常会抛出,导致抛出异常的代码终止执行。

  • 和 Java 不同的是,所有的 Dart 异常是非检查异常。

  • 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。

详情请参考 Exceptions 部分。

2、类型

注意:Dart 代码可以抛出任何非null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。

3、Throw(抛出异常)

  • 可以抛出任意对象:throw 'Out of llamas!';
  • 可以使用箭头函数=>: 抛出异常是一个表达式,所以可以在 => 语句中使用,也可以在其他能使用表达式的地方抛出异常。``` distanceTo(Point other) => throw new UnimplementedError();

4、Catch(捕获异常)

  • 异常捕获的关键字
    on :捕获异常,指定异常类型
    catch:捕获异常,捕获异常对象。
    rethrow:重新抛出异常

  • 说明:
    1、捕获异常可以避免异常继续传递(你重新抛出rethrow异常除外)。捕获异常给你一个处理该异常的机会。
    2、对于可以抛出多种类型异常的代码,你可以指定多个捕获语句。每个语句分别对应一个异常类型,如果捕获语句没有指定异常类型,则该可以捕获任何异常类型。
    3、函数 catch() 可以带有一个或者两个参数,第一个参数为抛出的异常对象,第二个为堆栈信息 (一个 StackTrace 对象)。

  • 示例:

     var foo = '';
    
       void misbehave() {
           try {
               foo = "You can't change a final variable's value.";
               //此处演示异常
               String sub = foo.substring(100);
           } on Exception catch (e) {
               print('Exception details:\n $e');
           } catch (e, s) {
               print('misbehave() partially handled ${e.runtimeType}.');
               print('Stack trace:\n$s');
               rethrow; // Allow callers to see the exception.
           }
       }
    
       void main() {
           try {
               misbehave();
           } catch (e) {
               print('main() finished handling ${e.runtimeType}.');
           }
       }
    
       /**
        *
        打印结果:
           misbehave() partially handled RangeError.
           Stack trace:
           #0      _StringBase.substring (dart:core/runtime/libstring_patch.dart:384:7)
           #1      misbehave (file:///Users/yu/Work/Workplace/Flutter/Dart/lesson2/exception_test.dart:7:26)
           #2      main (file:///Users/yu/Work/Workplace/Flutter/Dart/lesson2/exception_test.dart:19:9)
           #3      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:300:19)
           #4      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
    
           main() finished handling RangeError.
        */ 

5、Finally

  • 要确保某些代码执行,不管有没有出现异常都需要执行,可以使用 一个 finally 语句来实现。

  • 如果没有 catch 语句来捕获异常,则在执行完 finally 语句后,异常被抛出了。

  • 定义的 finally 语句在任何匹配的 catch 语句之后执行。

  • 示例:

     try {
            String sub = "123".substring(10);
        } catch(e) {
            print('Exception details:\n$e');
        } finally {
            print("result........");
        } 

十、元数据

1、简介:

  • 使用元数据可以给你的代码添加其他额外信息。
  • 元数据可以在 libraryclasstypedeftype parameterconstructorfactoryfunctionfieldparameter、或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。
  • 使用反射可以在运行时获取元数据 信息。

2、定义:

  • 元数据注解是以 @ 字符开头,后面是一个编译时常量(例如 deprecated)。
  • 调用一个常量构造函数。

形如:@deprecated@override

3、类型:

  • Dart内置元数据注解:
    @deprecated
    @override
    @proxy

     class Television {
         /// _Deprecated: Use [turnOn] instead._
         @deprecated
         void activate() {
           turnOn();
         }
    
         /// Turns the TV's power on.
         void turnOn() {
           print('on!');
         }
       } 
  • 自定义元数据注解:

     library todo;
    
       class todo {
         final String who;
         final String what;
    
         const todo(this.who, this.what);
       } 

    使用 @todo 注解的示例:

     import 'todo.dart';
    
       @todo('seth', 'make this do something')
       void doSomething() {
         print('do something');
       } 
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
待兔
待兔
Lv1
男 · helloworld公司 · CTO - helloworld开发者社区站长
helloworld开发者社区网站站长
文章
89
粉丝
43
获赞
75