给自己看的Dart笔记,看不懂别找我

Default value(默认值)

没有初始化的变量自动获取一个默认值为 null。类型为数字的 变量如何没有初始化其值也是 null,不要忘记了 数字类型也是对象。

int lineCount;
assert(lineCount == null);
// Variables (even if they will be numbers) are initially null.

Final and const

如果你以后不打算修改一个变量,使用 final 或者 const。 一个 final 变量只能赋值一次;一个 const 变量是编译时常量。 (Const 变量同时也是 final 变量。) 顶级的 final 变量或者类中的 final 变量在 第一次使用的时候初始化。

注意: 实例变量可以为 final 但是不能是 const 。

const 关键字不仅仅只用来定义常量。 还可以用来创建不变的值, 还能定义构造函数为 const 类型的,这种类型 的构造函数创建的对象是不可改变的。任何变量都可以有一个不变的值。

Strings(字符串)

通过提供一个 r 前缀可以创建一个 “原始 raw” 字符串:

var s = r"In a raw string, even \n isn't special.";

原始 raw 字符串:字符串里的所有字符都会被当成字符输出。上面的字符串 s,输出如下:In a raw string, even \n isn’t special.,里面的换行符 “\n” 也会被输出,而不是输出一个空行。

Lists(列表)

list 字面量之前添加 const 关键字,可以 定义一个不变的 list 对象(编译时常量):

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.

Runes

Dart 中,runes 代表字符串的 UTF-32 code points

Unicode 为每一个字符、标点符号、表情符号等都定义了 一个唯一的数值。 由于 Dart 字符串是 UTF-16 code units 字符序列, 所以在字符串中表达 32-bit Unicode 值就需要 新的语法了。

通常使用 \uXXXX 的方式来表示 Unicode code point, 这里的 XXXX 是4个 16 进制的数。 例如,心形符号 (♥)\u2665。 对于非 4 个数值的情况, 把编码值放到大括号中即可。 例如,笑脸 emoji (?)\u{1f600}

String 类 有一些属性可以提取 rune 信息。 codeUnitAtcodeUnit 属性返回 16-bit code units, 使用 runes 属性来获取字符串的 runes 信息。

Optional parameters(可选参数)

可选参数可以是命名参数或者基于位置的参数,但是这两种参数不能同时当做可选参数。

Optional named parameters(可选命名参数)

调用方法的时候,你可以使用这种形式 paramName: value 来指定命名参数。例如:

enableFlags(bold: true, hidden: false);

在定义方法的时候,使用 {param1, param2, …} 的形式来指定命名参数:

/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags({bool bold, bool hidden}) {
  // ...
}

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

/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
  // ...
}

// bold will be true; hidden will be false.
enableFlags(bold: true);
Optional positional parameters(可选位置参数)

把一些方法的参数放到 [] 中就变成可选 位置参数了:

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

对于可选命名参数和可选位置参数的区别,目前来看仅仅是调用的时候可选命名参数需要写上名字,而可选位置参数是不需要的。

Cascade notation (..)(级联操作符)

级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。 使用级联操作符可以避免创建 临时变量, 并且写出来的代码看起来 更加流畅:

例如下面的代码:

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法 querySelector() 返回了一个 selector 对象。 后面的级联操作符都是调用这个对象的成员, 并忽略每个操作 所返回的值。

Note:
1、方法的返回如果为void,使用级联不起作用;
2、String的相关方法,使用级联不起作用;

Constructors

Default constructors(默认构造函数)

如果你没有定义构造函数,则会有个默认构造函数。 默认构造函数没有参数,并且会调用超类的 没有参数的构造函数。

Constructors aren’t inherited(构造函数不会继承)

子类不会继承超类的构造函数。 子类如果没有定义构造函数,则只有一个默认构造函数 (没有名字没有参数)。

Named constructors(命名构造函数)

使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图:

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Named constructor
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

注意:构造函数不能继承,所以超类的命名构造函数 也不会被继承。如果你希望 子类也有超类一样的命名构造函数,你必须在子类中自己实现该构造函数。

Redirecting constructors(重定向构造函数)

有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。

class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}
Constant constructors(常量构造函数)

如果你的类提供一个状态不变的对象,你可以把这些对象 定义为编译时常量。要实现这个功能,需要定义一个 const 构造函数, 并且声明所有类的变量为 final。

class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
}
Factory constructors(工厂方法构造函数)

如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。

下面代码演示工厂构造函数 如何从缓存中返回对象。

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to the _ in front
  // of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String 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);
    }
  }
}

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

使用 new 关键字来调用工厂构造函数。

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

Invoking a non-default superclass constructor(调用超类构造函数)

默认情况下,子类的构造函数会自动调用超类的 无名无参数 的默认构造函数。 超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。 下面是构造函数执行顺序:

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

如果超类没有无名无参数构造函数, 则你需要手工的调用超类的其他构造函数。 在构造函数参数后使用冒号 “:” 可以调用 超类构造函数。
初始化列表非常适合用来设置 final 变量的值。 下面示例代码中初始化列表设置了三个 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);
}

Getters and setters

Getters and setters are special methods that provide read and write access to an object’s properties. Recall that each instance variable has an implicit getter, plus a setter if appropriate. You can create additional properties by implementing getters and setters, using the get and set keywords:

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and 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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

With getters and setters, you can start with instance variables, later wrapping them with methods, all without changing client code.

Implicit interfaces

Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.

A class implements one or more interfaces by declaring them in an implements clause and then providing the APIs required by the interfaces. For example:

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
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()));
}

Overriding members

Subclasses can override instance methods, getters, and setters. You can use the @override annotation to indicate that you are intentionally overriding a member:

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

The covariant keyword

Some (rarely used) coding patterns rely on tightening a type by overriding a parameter’s type with a subtype, which is invalid. In this case, you can use the covariant keyword to tell the analyzer that you are doing this intentionally. This removes the static error and instead checks for an invalid argument type at runtime.

Version note: The covariant keyword was introduced in 1.22. It replaces the @checked annotation.

The following shows how you might use covariant:

class Animal {
  void chase(Animal x) { ... }
}

class Mouse extends Animal { ... }

class Cat extends Animal {
  void chase(covariant Mouse x) { ... }
}

Although this example shows using covariant in the subtype, the covariant keyword can be placed in either the superclass or the subclass method. Usually the superclass method is the best place to put it. The covariant keyword applies to a single parameter and is also supported on setters and fields.

noSuchMethod()

To detect or react whenever code attempts to use a non-existent method or instance variable, you can override noSuchMethod():

class Foo {
  foo(x) {}
}

class MockFoo implements Foo {
  // PS: Make sure that a tear-off of `_mockFoo` has the same type
  // as a tear-off of `Foo.foo`.
  _mockFoo(x) {
    // ... implement mock behavior for `foo` here.
  }

  noSuchMethod(Invocation i) {
    if (i.memberName == #foo) {
      if (i.isMethod &&
          i.positionalArguments.length == 1 &&
          i.namedArguments.isEmpty) {
        return _mockFoo(i.positionalArguments[0]);
      } else if (i.isGetter) {
        return _mockFoo;
      }
    }
    return super.noSuchMethod(i);
  }
}

You can’t invoke an unimplemented method unless one of the following is true:

  1. The receiver has the static type dynamic.
  2. The receiver has a static type that defines the unimplemented method
    (abstract is OK), and the dynamic type of the receiver has an
    implemention of noSuchMethod() that’s different from the one in
    class Object.

For more information, see the informal noSuchMethod forwarding specification.

Adding features to a class: mixins

Mixins are a way of reusing a class’s code in multiple class hierarchies.

To use a mixin, use the with keyword followed by one or more mixin names. The following example shows two classes that use mixins:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

To implement a mixin, create a class that extends Object and declares no constructors. Unless you want your mixin to be usable as a regular class, use the mixin keyword instead of class. For example:

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

To specify that only certain types can use the mixin — for example, so your mixin can invoke a method that it doesn’t define — use on to specify the required superclass:

mixin MusicalPerformer on Musician {
  // ···
}

Version note: Support for the mixin keyword was introduced in Dart 2.1. Code in earlier releases usually used abstract class instead. For more information on 2.1 mixin changes, see the Dart SDK changelog and 2.1 mixin specification.

Generic collections and the types they contain

Dart generic types are reified, which means that they carry their type information around at runtime. For example, you can test the type of a collection:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

Note: In contrast, generics in Java use erasure, which means that generic type parameters are removed at runtime. In Java, you can test whether an object is a List, but you can’t test whether it’s a List.

Asynchrony support

Handling Futures

When you need the result of a completed Future, you have two options:

  1. Use async and await.
  2. Use the Future API, as described in the library tour.

Code that uses async and await is asynchronous, but it looks a lot like synchronous code. For example, here’s some code that uses await to wait for the result of an asynchronous function:

await lookUpVersion();

To use await, code must be in an async function—a function marked as async:

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

Note: Although an async function might perform time-consuming operations, it doesn’t wait for those operations. Instead, the async function executes only until it encounters its first await expression (details). Then it returns a Future object, resuming execution only after the await expression completes.

Callable classes

To allow your Dart class to be called like a function, implement the call() method.

void main() {
  var demo = CallableClass();
  print(demo(1, 2));
}

class CallableClass {
  int call(int a, int b) => a + b;
}

Async and await

The following diagram shows the flow of execution through the code. Each number corresponds to a step below.

diagram showing flow of control through the main() and printDailyNewsDigest functions
在这里插入图片描述

  1. The app begins executing.
  2. The main() function calls the async function printDailyNewsDigest(),
    which begins executing synchronously.
  3. printDailyNewsDigest() uses await to call the function
    gatherNewsReports(), which begins executing.
  4. The gatherNewsReports() function returns an uncompleted future (an
    instance of Future<String>).
  5. Because printDailyNewsDigest() is an async function and is awaiting
    a value, it pauses its execution and returns an uncompleted future
    (in this case, an instance of Future<void>) to its caller (main()).
  6. The remaining print functions execute. Because they’re synchronous,
    each function executes fully before moving on to the next print
    function. For example, the winning lottery numbers are all printed
    before the weather forecast is printed.
  7. When main() has finished executing, the asynchronous functions can
    resume execution. First, the future returned by gatherNewsReports()
    completes. Then printDailyNewsDigest() continues executing, printing
    the news.
  8. When the printDailyNewsDigest() function body finishes executing,
    the future that it originally returned completes, and the app exits.

Note that an async function starts executing right away (synchronously). The function suspends execution and returns an uncompleted future when it reaches the first occurrence of any of the following:

  • The function’s first await expression (after the function gets the uncompleted future from that expression).
  • Any return statement in the function.
  • The end of the function body.

只有执行完main方法下面的代码后才会执行那些await的操作!

async、async*、sync*

async与await配合使用;
sync*、async*与yield配合使用;

DO annotate with Object instead of dynamic to indicate any object is allowed.

Some operations work with any possible object. For example, a log() method could take any object and call toString() on it. Two types in Dart permit all values: Object and dynamic. However, they convey different things. If you simply want to state that you allow all objects, use Object, as you would in Java or C#.

Using dynamic sends a more complex signal. It may mean that Dart’s type system isn’t sophisticated enough to represent the set of types that are allowed, or that the values are coming from interop or otherwise outside of the purview of the static type system, or that you explicitly want runtime dynamism at that point in the program.

void log(Object object) {
  print(object.toString());
}

/// Returns a Boolean representation for [arg], which must
/// be a String or bool.
bool convertToBool(dynamic arg) {
  if (arg is bool) return arg;
  if (arg is String) return arg == 'true';
  throw ArgumentError('Cannot convert $arg to a bool.');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值