dart

作者: liboxiang | 来源:发表于2019-07-31 19:30 被阅读0次

"_"

  • 不同文件中才有私有效果,如果是同一.dart文件中,则没有私有效果。

可选参数

用[]包围的参数是可选参数,要放在参数列表后面,如下面device就是可选参数

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

函数相等

  • 同一函数,不管怎么赋值给变量,都相等
  • 如果将函数复制给一个对象的变量,则不同对象的该变量不相等。
void foo() {} // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {} // An instance method
}

void main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = A(); // Instance #1 of A
  var w = A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}

所有函数都有返回值

如果不指定返回值,则返回null

as和is(is!)

用于判断与形式类型

if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}

上面可以简写为

//当emp不是Person类型时,会报错。所以as可以理解为类型强转
(emp as Person).firstName = 'Bob';

??

// Assign value to b if b is null; otherwise, b stays the same
b ??= value;
expr1 ?? expr2
If expr1 is non-null, returns its value; otherwise, evaluates and returns the value of expr2.

级联符号".."

级联符号(..)允许对同一对象进行一系列操作。除了函数调用,还可以访问同一对象上的字段。这通常可以节省创建临时变量的步骤,并允许编写更多流畅的代码。

级联只能在返回实际对象的函数后面构建

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

错误用法,下面write方法返回void,不能用于构建级联

var sb = StringBuffer();
sb.write('foo')
  ..write('bar'); // Error: method 'write' isn't defined for 'void'.

continue用法,跳转到标签位置

var command = 'OPEN';
    switch (command) {
    case 'OPEN':
    // ERROR: Missing break
      continue here;
    case 'CLOSED':
        print('close');
        break;
      
    here:
    case 'other':
      print('other');
     break;
}

异常

抛出异常
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
捕获异常

既可以用on也可以用catch,如果需要指定异常类型则用on,如果需要异常对象则用catch

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

catch可以指定两个参数,第一个是异常对象,第二个是堆栈(StackTrace)

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

在处理异常的同时如果要允许异常继续传播可以使用rethrow

void misbehave() {
try {
  dynamic foo = true;
  print(foo++); // Runtime error
} catch (e) {
  print('misbehave() partially handled ${e.runtimeType}.');
  rethrow; // Allow callers to see the exception.
}
}

void main() {
try {
  misbehave();
} catch (e) {
  print('main() finished handling ${e.runtimeType}.');
}
}

finally
不管是否抛出异常,finally部分代码都会运行。如果没有catch匹配异常,则在finally运行后传递异常。

try {
  breedMoreLlamas();
} catch (e) {
  print('Error: $e'); // Handle the exception first.
} finally {
  cleanLlamaStalls(); // Then clean up.
}

类中的变量

如果变量初始化不是在构造函数或者方法中,则变量在类对象创建时被初始化,这是在构造函数和初始化列表之前的。

命名构造函数

class Point {
  num x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}

调用非默认的超类构造函数

调用顺序为

  • 1.初始化列表
  • 2.超类的无参数构造函数
  • 3.主类的无参数构造函数

因为在调用构造函数之前会计算超类构造函数的参数,所以参数可以是一个表达式,例如函数调用:

class Employee extends Person {
//getDefaultData()要为静态函数(static),不能为对象函数
  Employee() : super.fromJson(getDefaultData());
  // ···
}

初始化列表

除了调用父类的构造函数,在构造函数体运行前还可以进行初始化列表。初始化列表不能用到this

Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

构造函数中的:

允许初始化类的字段,进行断言并调用父类构造函数,重定向构造函数。

  • 初始化类的字段和调用父类构造函数如上。
  • 断言
Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}
  • 重定向构造函数
class Point {
  num x, y;

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

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

工厂构造函数

  • 关键字factory在实现不总是创建其类的新实例的构造函数时使用。例如,工厂构造函数可能从缓存中返回实例,或者它可能返回子类型的实例。
  • 工厂构造函数不能使用this
  • 如下例子从缓存中返回对象
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 = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

Getters and setters

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

抽象方法

抽象方法只存在抽象类中,抽象方法只是定义一个接口,其他类进行具体实现。

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

隐式接口

每个类都隐式定义一个接口,该接口包含该类的所有实例成员及其实现的任何接口。如果要在不继承B实现的情况下创建支持B类API的A类,则A类应实现B接口(用implements)。
使用implements要实现所有变量和方法

// 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. implements后可以跟多个类名
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()));
}

noSuchMethod()

接收访问不存在方法和变量的事件,如果不重写这个方法,当访问不存在的变量和方法的时候会报错

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

mixins

原理:Dart中的Mixins通过创建一个新的类来实现mixin的实现
在超类之上创建一个新类 - 它不是“在一边”而是“当前类顶部,当前类的super的下面”
,所以如何查找是没有歧义的。

要实现mixin,请创建一个扩展Object的类,并且不声明构造函数。除非您希望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');
    }
  }
}

要指定只有某些类型可以使用mixin,可以使用on来指定所需的超类:

mixin MusicalPerformer on Musician {
  // ···
}

实现原理举例:

如果with后面跟多个,且有同名方法,则以最后一个为准,如class AB extends P with A, B {}如果A、B有同名方法,则AB使用的是B的方法。如果mixin和extends中有同名方法,最终使用的是mixin中的

class AB extends P with A, B {}
class BA extends P with B, A {}

等于

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}

继承层级如下图:


1_sayzomK1biB7iy5W8hrUSQ.png
Demo
class Base {
  int num = 0;
  void attach(String s) {
    print("Base attach $s");
  }
}

class ViewBase extends Base {
  void attach(String s) {
    print("Viewbase attach $s");
    super.attach(s);
  }
}

mixin Mixin on ViewBase {
  void attach(String s) {
    
    print("Mixin attach");
    super.attach(s);
  }
}

mixin Mixin2 on ViewBase {
  void attach(String s) {
    print("Mixin2 attach $s");
    super.attach(s);
  }
}

class View extends ViewBase with Mixin,Mixin2 {
  void attach(String str) {
    print("View attach $str");
    super.attach(str);
  }
}

void main() {
  new View().attach("me");
  // print(View().num);
}

static

static修饰的变量直到被使用才会初始化。

泛型

dart支持java中的泛型

import

指定前缀
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
只导入一部分
// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
懒加载库

以下是您可能使用延迟加载的一些情况:

  • 减少应用程序的初始启动时间。
  • 例如,执行A / B测试 - 尝试算法的替代实现。
  • 加载很少使用的功能,例如可选的屏幕和对话框。
import 'package:greetings/hello.dart' deferred as hello;
//当需要使用到库的时候加载库,loadLibrary()可以多次调用,但是库只会加载一次
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

使用延迟加载时请记住以下内容:

  • 延迟库的常量不是导入文件中的常量。请记住,在加载延迟库之前,这些常量不存在。
  • 您不能在导入文件中使用延迟库中的类型。相反,请考虑将接口类型移动到由延迟库和导入文件导入的库。

异步

异步函数只在遇到第一个await表达式时执行,然后返回一个future对象。

生成器(Generators)

  • sync*描述的函数返回的是Iterable,为同步生成器
  • async*描述的函数返回的是Stream,为异步生成器
  • yield*用于递归上面两种函数
    当需要懒加载生成一系列数据的时候可以使用生成器
同步生成器
main() {
  var it = naturalsTo(5).iterator;
  while(it.moveNext()) {
    print(it.current);
  }
}

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}
异步生成器
import 'dart:async';

main() {
  // 流监听
  // asynchronousNaturalsTo(5).listen((onData) {
  //   print(onData);
  // });

  // 流监听 StreamSubscription 对象
  StreamSubscription subscription = asynchronousNaturalsTo(5).listen(null);
  subscription.onData((value) {
    print(value);
    /*
start
0
1
2
3
4
end
*/
  });
}

Stream<int> asynchronousNaturalsTo(int n) async* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}
递归生成器

2
3
4
5
6
7
8
9
10
11
12
13
main() {
  var it = naturalsDownFrom(5).iterator;
  while(it.moveNext()) {
    print(it.current);
  }
}

Iterable<int> naturalsDownFrom(int n) sync* {
  if ( n > 0) {
    yield n;
    yield* naturalsDownFrom(n-1);
  }
}

callable classes

实现call()方法

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out');
}

Typedefs

当前只能用于函数

typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

// Initial, broken implementation.
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}

流Stream

有两种流。

单个订阅流

最常见的流包含一系列事件,这些事件是更大整体的一部分。事件需要以正确的顺序传递,而不会遗漏任何事件。这是您在读取文件或接收Web请求时获得的流。

这样的流只能被听一次。稍后再次倾听可能意味着错过了初始事件,然后流的其余部分毫无意义。当您开始收听时,将获取数据并以块的形式提供。

广播流

另一种流用于可以一次处理一个的单个消息。例如,这种流可以用于浏览器中的鼠标事件。

您可以随时开始收听此类流,并获得在收听时触发的事件。多个听众可以同时收听,您可以在取消之前的订阅后再次收听。

相关文章

网友评论

      本文标题:dart

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