Flutter

Dart 语法

2021-03-12  本文已影响0人  哦呵呵y

重要的概念

语法

变量与常量

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

内建类型

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 的两段式初始化):

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

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

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

枚举

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

枚举类型具有以下限制:

上一篇下一篇

猜你喜欢

热点阅读