Android学Dart学习笔记第十二节 函数
读取属性调用getter函数,编写属性调用setter函数,即使在属性被声明为变量的情况下也是如此。在上面的例子中,我们在main函数中声明了print1,在print1中声明了print2, 在print2函数中我们可以调用所有父函数的局部变量,但在main函数中不可以跨层级调用print2函数。对于任何一个语言的程序员来说,在任意一门语言中定义一个函数都不会是一个难事,但是我们任然需要去学习d
序言
对于任何一个语言的程序员来说,在任意一门语言中定义一个函数都不会是一个难事,但是我们任然需要去学习dart的函数定义,不是因为我们不会,而是也许可以更好
基本语法
你所知道,或者不知道的函数定义方式
下面3个函数,干的都是一个事,但写法却有较大差异。
bool isEven(int a) {
return a % 2 == 0;
}
isEven2(a) {
return a % 2 == 0;
}
isEven3(a) => a % 2 == 0;
isEven是最常规的写法,它明确的声明了参数类型,具有很强的类型约束。
isEven2省略了类型,包括入参和返回值,但他仍然可以在特定类型下正常运行。但不利于他人调用
isEven3在isEven2的基础上使用了速记语法又叫箭头语法,当方法体仅有一个表达式时,它可以看起来很简洁
在箭头语法中下面的写法是不可以的,箭头语法的方法体只能是表达式
函数的参数
函数的参数分为三类,参数可以有任意个required positional parameters(必须参数),后面可以有Named parameters 和optional positional parameters,但不可以都有。
这是最常规的参数定义
int sum(int a, int b) {
return a + b;
}
他的参数全是必须参数
Named parameters 命名参数
命名参数使用{param1, param2, …}的形式定义
- 可以指定默认值,
- 也可以使用?定义可选参数,
- 调用时必须使用参数名:值的方式指定参数值
如果你希望某个参数在被调用时必传,需要使用required关键字
被标记为required的参数,任然可以设置允许为空,此时调用时任然必穿,但可以传null
printSum(a : 1, b: 2, c: null);
void printSum({int a = 0, int? b, required String? c}) {
print('$a + $b = ${a + (b ?? 0)}');
}
在函数定义时,命名参数必须在必须参数之后定义,但在传参时可以在任何位置
Optional positional parameters 可选位置参数
可选位置参数使用[param1, param2, …]的形式声明,该参数必须设置默认值或者允许null

默认值只能是编译时常数。
The main() function
每个应用程序都必须有一个顶级main()函数,用作应用程序的入口点。main()函数返回void并具有可选的List参数作为参数,但是不可以同时存在2个main方法。
Dart中是不支持方法重载的。
void main() {
print('hello world');
}
void main(List<String> args) {
print(args);
}
Functions as first-class objects
函数也是个类对象
回顾一下我们之前用过的可迭代容器的foreach方法,他的参数就是函数类型
var list = <int>[];
list.forEach(action)
下面是官方foreach的实现代码
我们参照他的写法来实现类似的
这应该比较容易理解哈,我在kotlin就喜欢这么写,dart语法不太一样,但功能类似。
也可以将函数声明为一个变量
此时onClick的类型如下
Function types
在dart中可以使用函数类型,声明方式如下:
setOnClickListener3(OnClickListener(onClick));
class OnClickListener {
void Function(String param) onClick;
OnClickListener(this.onClick);
}
void Function(String param) onClick;
在定义函数类型时,可以省略必选参数和可选位置参数的名称,但命名参数的名称不可省略
Anonymous functions 匿名函数
我们前面也用到了
setOnClickListener3(OnClickListener((s, [a, b]) => print('$s $a')));
OnClickListener是个类对象,他的构造参数是函数类型,此时我们创建了一个匿名函数给他。
Lexical scope 词法范围
dart中函数也是对象,所以对象中可以有对象,函数中也可以有函数
在上面的例子中,我们在main函数中声明了print1,在print1中声明了print2, 在print2函数中我们可以调用所有父函数的局部变量,但在main函数中不可以跨层级调用print2函数。
Lexical closures闭包
简单来说,就是每个函数都是独立运行的,不受其他调用者的影响。
Tear-offs 方法抽取
直接看例子,文字已经不好说明了
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();

下面是一个更偏向实际的例子
// 传统方式
button.onClick((event) {
myHandler.handleClick(event);
});
// 使用 tear-off(更简洁)
button.onClick(myHandler.handleClick);
个人理解就是我们不需要造壳子。
Testing functions for equality 函数相等测试
- 顶层函数:相同函数总是相等
- 静态方法:相同静态方法总是相等
- 实例方法(闭包):
相同实例的相同方法 → 相等
不同实例的相同方法 → 不相等
void foo() {} // A top-level function(顶层函数)
class A {
static void bar() {} // A static method(静态方法)
void baz() {} // An instance method(实例方法)
}
void main() {
Function x;
// 1. 比较顶层函数
x = foo; // tear-off:获取函数引用
assert(foo == x); // ✅ 相等:相同的顶层函数
// 2. 比较静态方法
x = A.bar; // tear-off:获取静态方法引用
assert(A.bar == x); // ✅ 相等:相同的静态方法
// 3. 比较实例方法(闭包)
var v = A(); // Instance #1 of A(实例1)
var w = A(); // Instance #2 of A(实例2)
var y = w; // y 和 w 引用同一个实例(实例2)
x = w.baz; // tear-off:获取实例2的baz方法
// 这些闭包引用相同的实例(实例2),所以相等
assert(y.baz == x); // ✅ 相等:y 和 w 是同一实例
// 这些闭包引用不同的实例,所以不相等
assert(v.baz != w.baz); // ✅ 不相等:v 和 w 是不同的实例
}
Return values 返回值
在dart中所有函数都有返回值,void也算,未明确指定的情况下会返回null
foo() {}
assert(foo() == null);
可以返回多个值,通过record
(String, int) foo() {
return ('something', 42);
}
Setter&Getter
每个属性访问(顶级、静态或实例)都是对getter或setter的调用。变量隐式创建一个getter,如果它是可变的,则创建一个setter。这就是为什么当您访问属性时,您实际上是在后台调用一个小函数。读取属性调用getter函数,编写属性调用setter函数,即使在属性被声明为变量的情况下也是如此。
如果我们想要为一个变量手动声明set or get方法,参考下面的代码
// Defines a variable `_secret` that is private to the library since
// its identifier starts with an underscore (`_`).
String _secret = 'Hello';
// A public top-level getter that
// provides read access to [_secret].
String get secret {
print('Getter was used!');
return _secret.toUpperCase();
}
// A public top-level setter that
// provides write access to [_secret].
set secret(String newMessage) {
print('Setter was used!');
if (newMessage.isNotEmpty) {
_secret = newMessage;
print('New secret: "$newMessage"');
}
}
void main() {
// Reading the value calls the getter.
print('Current message: $secret');
/*
Output:
Getter was used!
Current message: HELLO
*/
// Assigning a value calls the setter.
secret = 'Dart is fun';
// Reading it again calls the getter to show the new computed value
print('New message: $secret');
/*
Output:
Setter was used! New secret: "Dart is fun"
Getter was used!
New message: DART IS FUN
*/
}
如果你觉得上面的复杂,可以看看我自己写的

这段代码中name字段被定义为私有,禁止外部访问,通过set和get关键字定义了面向外部的set,get函数。
通过这样的方式,可以实现权限控制,参数有效性控制,内容二次加工等需求。
Generators
当你需要延迟生成一系列值时,可以考虑使用生成器函数。Dart内置支持两种生成器函数:
同步生成器:返回一个可迭代对象。 Iterable
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
异步生成器:返回一个流对象。Stream
Stream<int> naturalsToStream(int n) async* {
int k = 0;
while (k < n) {
print(k);
yield k++;
}
}
Stream的使用方式
当递归生产值时,需要使用yield*
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
External functions 外部函数
指方法的申明和实现分开的,比如和其他语言互操作,有点类似java中调用c的native关键字。
声明方式如下
external void someFunc(int i);
小结
还是学到了不少东西哈。
更多推荐



所有评论(0)