Dart 语法

作者: 哦呵呵y | 来源:发表于2021-03-12 15:16 被阅读0次

    重要的概念

    • 任何保存在变量中的都是一个 对象 , 并且所有的对象都是对应一个 的实例。 无论是数字,函数和 null 都是对象。所有对象继承自 Object 类。

    • 尽管 Dart 是强类型的,但是 Dart 可以推断类型,所以类型注释是可选的。var number = 42;number 被推断为 int 类型。 如果要明确说明不需要任何类型, 需要使用特殊类型 dynamic

    • Dart 支持泛型,如 List <int> (整数列表)或 List <dynamic> (任何类型的对象列表)。

    • Dart 支持顶级函数(例如 main() ), 同样函数绑定在类或对象上(分别是 静态函数实例函数 )。 以及支持函数内创建函数 ( 嵌套局部函数 ) 。

    • 类似地, Dart 支持顶级 变量 , 同样变量绑定在类或对象上(静态变量和实例变量)。 实例变量有时称为字段或属性。

    • 与 Java 不同,Dart 没有关键字 “public” , “protected” 和 “private” 。 如果标识符以下划线(_)开头,则它相对于库是私有的(私有方法必须要抽离在单独文件中,否则不生效。)。 有关更多信息,参考 库和可见性

    • 标识符 以字母或下划线(_)开头,后跟任意字母和数字组合。

    • Dart 语法中包含 表达式( expressions )(有运行时值)和 语句( statements )(没有运行时值)。 例如,条件表达式 condition ? expr1 : expr2 的值可能是 expr1expr2 。 将其与 if-else 语句 相比较,if-else 语句没有值。 一条语句通常包含一个或多个表达式,相反表达式不能直接包含语句。

    • Dart 工具提示两种类型问题:警告错误。 警告只是表明代码可能无法正常工作,但不会阻止程序的执行。 错误可能是编译时错误或者运行时错误。 编译时错误会阻止代码的执行; 运行时错误会导致代码在执行过程中引发 [异常](#exception)。

    语法

    变量与常量

    1. 变量初始化
    // 没有明确类型,编译的时候根据值明确类型
    var name = 'Bob';
    dynamic name = 'Bob';
    Object name = 'Bob'
    
    // 显示声明将被推断类型, 可以使用String显示声明字符串类型
    String name = 'Bob' ;
    
    2. 默认值

    未初始化的变量默认值是 null。即使变量是数字 类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型 也不例外。

    3. Object 和 dynamic
      dynamic a = 'string';
      a = 10;
      a = 1.1;
      a.foo();
    
      Object b = 'string';
      b = 10;
      b = 1.1;
      // 编译错误 "The method 'foo' isn't defined for the type 'Object'."
      // b.foo();
    
    
    1. dynamic和object类型是可以变的
    2. Object 是静态类型检测的,所以Object 对象只能调用Object 的方法,调用其他方法会产生编译错误
    3. dynamic是运行时检测的,所以可以调用任何方法,(注意会产生运行时错误,尽量避免使用)
    4. Final 和 Const
    1. 使用过程中从来不会被修改的变量, 可以使用 final 或 const
    2. 实例变量可以是 final 类型但不能是 const 类型。 必须在构造函数体执行之前初始化 final 实例变量
    3. Final 变量的值只能被设置一次, 最高级 final 变量或类变量在第一次使用时被初始化(运行时)
    class Name {
      // 1. 直接设置默认值
      final a = 10;
      
      // 2. 在初始化列表中初始化
      final a;
      Name(this.a)
    }
    
    1. Const 变量在编译时就已经固定 (Const 变量 是隐式 Final 的类型.)
    2. Const 变量必须由常量初始化
      var a = 10;
      const b = 10;
    
      // 编译报错 "Const variables must be initialized with a constant value."
      const c = a;
    
      const d = b;
    
    1. 在集合字面量之前添加 const 关键字,可以定义编译时常量
      var a = const [1, 2, 3];
      var b = const {1, 2, 3};
      var c = const {1: 1, 2: 2};
    

    内建类型

    • Dart 语言支持以下内建类型:
    • Number
    • String
    • Boolean
    • List (也被称为 Array)
    • Map
    • Set
    • Rune (用于在字符串中表示 Unicode 字符)
    • Symbol
    1. Number
    1. Dart 语言的 Number 有两种类型: int double
    2. int 整数值不大于64位, 具体取决于平台
    3. double 64位(双精度)浮点数
    2. String
    1. Dart 字符串是一组 UTF-16 单元序列。 字符串通过单引号或者双引号创建。
    2. 字符串可以通过 ${expression} 的方式内嵌表达式。 如果表达式是一个标识符,则 {} 可以省略。
    3. Flutter中字符串格式化只有插值,可以借助第三方库sprintf来实现格式化字符串
    3. Boolean
    1. Dart 使用 bool 类型表示布尔值。 Dart 只有字面量 true and false 是布尔类型, 这两个对象都是编译时常量。
    2. Dart 的类型安全意味着不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。
    3. Set
      var names = Set();
      var names = Set<String>();
      var names = <String>{};
      // var names = {}; // 这样会创建一个 Map ,而不是 Set 。  
    

    函数

    函数也是对象,并且有它的类型 Function。这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。 也可以把 Dart 类的实例当做方法来调用。

    1. 如果函数中只有一句表达式,可以使用简写语法:
    bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
    
    1. 在箭头 (=>) 和分号 (;) 之间只能使用一个 表达式 ,不能是 语句。
    2. 函数有两种参数类型: required 和 optional。 required 类型参数在参数最前面, 随后是 optional 类型参数。 命名的可选参数也可以标记为 “@ required”
    3. 所有函数都有返回值,没有明确指定返回值,函数隐式添加return null;
    1. 命名可选参数
    1. 定义函数是,使用 {param1, param2, …} 来指定命名参数
    void enableFlags({bool bold, bool hidden}) {...}
    
    1. 调用函数时,可以使用指定命名参数 paramName: value,
    enableFlags(bold: true, hidden: false);
    
    2. 位置可选参数
    1. 定义函数是,使用 [param1, param2, …] 来指定位置可选参数
    void enableFlags([bool bold, bool hidden]) {...}
    
    1. 调用函数, 自动按顺序将数据赋值给位置参数
    enableFlags(true, true);
    
    3. 默认参数值

    在定义方法的时候,可以使用 = 来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。

    1. 可选参数才能设置默认值
    // 位置可选
    void enableFlags([bool bold = false, bool hidden = false]) { ... }
    // 命名可选
    void enableFlags({bool bold = false, bool hidden = false}) { ... }
    
    1. 位置可选参数和命名可选参数不能混用。
    4. 匿名函数(block)
    // 1
    var testFunc = (String a) { ... };
    // 2
    Function(String a) testFunc;
    testFunc = (String a) { ... };
    // 3 
    bool Function(String a) testFunc;
    testFunc = (String a) { ... };
    // 4 
    typedef MyFunc = bool Function(String a);
    MyFunc testFunc;
    

    运算符

    1. 级联运算符 (..) 可以实现对同一个对像进行一系列的操作。 除了调用函数, 还可以访问同一对象上的字段属性。 这通常可以节省创建临时变量的步骤, 同时编写出更流畅的代码。
    querySelector('#confirm') // 获取对象。
      ..text = 'Confirm' // 调用成员变量。
      ..classes.add('important')
      ..onClick.listen((e) => window.alert('Confirmed!'));
    
    1. switch 和 case
      在非空 case必须加break、continue、thow或return
      空case允许程序以 fall-through 的形式执行
      非空case 实现 fall-through 需要使用 continue 语句结合 lable 的方式实现:
    var command = 'CLOSED';
    switch (command) {
      case 'CLOSED':
        executeClosed();
        continue nowClosed;
      // Continues executing at the nowClosed label.
    
      nowClosed:
      case 'NOW_CLOSED':
        // Runs for both CLOSED and NOW_CLOSED.
        executeNowClosed();
        break;
    }
    

    异常

    1. Dart 中的所有异常是非检查异常。 方法不会声明它们抛出的异常, 也不要求捕获任何异常。
    2. Dart 提供了 ExceptionError 类型, 以及一些子类型。 当然也可以定义自己的异常类型。 但是,此外 Dart 程序可以抛出任何非 null 对象, 不仅限 Exception 和 Error 对象。
    // 抛出异常
    throw FormatException('Expected at least 1 section');
    throw 'Out of llamas!';
    
    捕获
    try {
      // ···
    } on Exception catch (e) {
      print('Exception details:\n $e');
    } catch (e, s) {
      print('Exception details:\n $e');
      print('Stack trace:\n $s');
    } finally {
      // Always clean up, even if an exception is thrown.
      cleanLlamaStalls();
    }
    
    1. 捕获语句中可以同时使用 on 和 catch ,也可以单独分开使用。 使用 on 来指定异常类型, 使用 catch 来 捕获异常对象。
    2. catch() 函数可以指定1到2个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 ( 一个 StackTrace 对象 )。

    1. 构造函数
    1. 通过 构造函数 创建对象。 构造函数的名字可以是 ClassName 或者 ClassName.identifier。
    class Point {
      final x;
      final y;
      Point(this.x, this.y);
      Point.fromJson(this.x, this.y);
    }
    
    var p1 = Point(2, 2);
    var p2 = Point.fromJson({'x': 1, 'y': 2});
    
    1. 构造函数实例变量赋值简写
    class Point {
      num x, y;
    // 1 手动初始化属性
      Point(num x, num y) {
        // 还有更好的方式来实现下面代码,敬请关注。
        this.x = x;
        this.y = y;
      }
    // 2 自动初始化属性
      Point(this.x, this.y)
    // 3. 初始化列表
      Point(int x, int y) : this.x = x, this.y = y;
    }
    
    
    1. 在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。
    2. 子类不会继承父类的构造函数。 子类不声明构造函数,那么它就只有默认构造函数 (匿名,没有参数) 。
    3. 使用命名构造函数可为一个类实现多个构造函数, 也可以使用命名构造函数来更清晰的表明函数意图
    4. 常量构造函数
      const Point.ImmutablePoint(this.x, this.y);
    
      var a = const Point.ImmutablePoint(10, 11);
      var b = const Point.ImmutablePoint(10, 11);
      print(identical(a, b));  // true
    

    包含常量构造函数的类中只能包含final属性
    在常量构造函数前加const 会创建出唯一编译时常量

    1. 工厂构造函数(可以手动返回一个对象
      当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。
      以下示例演示了从缓存中返回对象的工厂构造函数(工厂构造函数无法访问 this。):
    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);
      }
    }
    

    使用工厂方法实现单例

      Singleton._privateConstructor();
    
      static final Singleton _instance = Singleton._privateConstructor();
    
      factory Singleton(){
        return _instance;
      }
    

    工厂方法 只是为了手动返回一个实例, 下面两种实现等价。

    // 工厂方法 只是为了手动返回一个实例
    class Test {
      Test._internal();
    
      static final Test _instance = Test._internal();
    
      // 1.
      static Test init1() {
        return _instance;
      }
    
      // 2.
      factory Test.init2() {
        return _instance;
      }
    }
    
    2. 调用父类非默认构造函数

    默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参数)。 父类的构造函数在子类构造函数体开始执行的位置被调用。 如果提供了一个 initializer list (初始化参数列表), 则初始化参数列表在父类构造函数执行之前执行。 总之,执行顺序如下(类似于Swift 的两段式初始化):

    • initializer list (初始化参数列表)
    • superclass’s no-arg constructor (父类的无名构造函数)
    • main class’s no-arg constructor (主类的无名构造函数)

    如果父类中没有匿名无参的构造函数, 则需要手工调用父类的其他构造函数。 在当前构造函数冒号 (:) 之后,函数体之前,声明调用父类构造函数。

    class Person {
      String firstName;
    
      Person.fromJson(Map data) {
        print('in Person');
      }
    }
    
    class Employee extends Person {
      // Person does not have a default constructor;
      // you must call super.fromJson(data).
      Employee.fromJson(Map data) : super.fromJson(data) {
        print('in Employee');
      }
    }
    
    3. 实例变量
    class Point {
      num x; // 声明示例变量 x,初始值为 null 。
      num y; // 声明示例变量 y,初始值为 null 。
      num z = 0; // 声明示例变量 z,初始值为 0 。
    }
    
    1. 未初始化实例变量的默认人值为 “null” 。
    2. 所有实例变量都生成隐式 getter 方法。 非 final 的实例变量同样会生成隐式 setter 方法。 有关更多信息,参考 Getters 和 setters.
    3. 如果在声明时进行了实例变量的初始化, 那么初始化值会在实例创建时赋值给变量, 该赋值过程在构造函数及其初始化列表执行之前。
    4. Getter 和 Setter
    class Rectangle {
      num left, top, width, 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;
    }
    
    5. 抽象类
    abstract class Doer {
      // 定义实例变量和方法 ...
    
      void doSomething(); // 定义一个抽象方法。
    }
    
    1. 抽象类不能实例化
    2. 抽象类中包含抽象方法,普通方法。抽象方法必须被重写
    6. 隐式接口

    在Dart 中没有interface。每个Class都是一个隐式接口。
    使用implements 实现接口

    // person 类。 隐式接口里面包含了 greet() 方法声明。
    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  {...}
    
    7. 继承
    1. Dart 是单继承。使用 extends 关键字来创建子类, 使用 super 关键字来引用父类:
    class Television {
      void turnOn() {
        _illuminateDisplay();
        _activateIrSensor();
      }
      // ···
    }
    
    class SmartTelevision extends Television {
      void turnOn() {
        super.turnOn();
        _bootNetworkInterface();
        _initializeMemory();
        _upgradeApps();
      }
      // ···
    }
    
    1. 子类可以重写实例方法,getter 和 setter。 可以使用 @override 注解指出想要重写的成员:
    class SmartTelevision extends Television {
      @override
      void turnOn() {...}
      // ···
    }
    
    8. 为类添加功能: Mixin

    Mixin 是复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。

    1. 通过 with 后面跟一个或多个混入的名称,来 使用 Mixin , 下面的示例演示了两个使用 Mixin 的类:
    class Maestro extends Person
        with Musical, Aggressive, Demented {
      Maestro(String maestroName) {
        name = maestroName;
        canConduct = true;
      }
    }
    
    1. 通过创建一个继承自 Object 且没有构造函数的类,来 实现 一个 Mixin 。 如果 Mixin 不希望作为常规类被使用,使用关键字 mixin 替换 class 。 例如:
    mixin Musical {
      bool canPlayPiano = false;
      bool canCompose = false;
      bool canConduct = false;
    
      void entertainMe() {
        if (canPlayPiano) {
          print('Playing piano');
        } else if (canConduct) {
          print('Waving hands');
        } else {
          print('Humming to self');
        }
      }
    }
    
    1. 指定只有某些类型可以使用的 Mixin - 比如, Mixin 可以调用 Mixin 自身没有定义的方法 - 使用 on 来指定可以使用 Mixin 的父类类型:
    mixin MusicalPerformer on Musician {
      // ···
    }
    
    9. noSuchMethod()

    当代码尝试使用不存在的方法或实例变量时, 通过重写 noSuchMethod() 方法,来实现检测和应对处理:

    class A {
      // 如果不重写 noSuchMethod,访问
      // 不存在的实例变量时会导致 NoSuchMethodError 错误。
      @override
      void noSuchMethod(Invocation invocation) {
        print('You tried to use a non-existent member: ' +
            '${invocation.memberName}');
      }
    }
    

    除非符合下面的任意一项条件, 否则没有实现的方法不能够被调用:

    • receiver 具有 dynamic 的静态类型 。
    • receiver 具有静态类型,用于定义为实现的方法 (可以是抽象的), 并且 receiver 的动态类型具有 noSuchMethod() 的实现, 该实现与 Object 类中的实现不同。

    枚举

    枚举中的每个值都有一个 index getter 方法, 该方法返回值所在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的索引是 0 , 第二个枚举值的索引是 1。

    枚举类型具有以下限制:

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

    相关文章

      网友评论

        本文标题:Dart 语法

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