"_"
- 不同文件中才有私有效果,如果是同一.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请求时获得的流。
这样的流只能被听一次。稍后再次倾听可能意味着错过了初始事件,然后流的其余部分毫无意义。当您开始收听时,将获取数据并以块的形式提供。
广播流
另一种流用于可以一次处理一个的单个消息。例如,这种流可以用于浏览器中的鼠标事件。
您可以随时开始收听此类流,并获得在收听时触发的事件。多个听众可以同时收听,您可以在取消之前的订阅后再次收听。
网友评论