方法(函数)

方法定义

Dart是一个面向对象的语言,因此函数也是对象,属于Function对象。方法定义格式如下:

1
2
3
4
返回类型 方法名(参数1,参数2,...) {
方法体...
return 返回值;
}

可以看到它其实和Java中方法的定义很像,所不同的是最前面没有方法的访问修饰符。

Dart中的方法的返回值类型和参数类型都可以省略。这里面还有一个箭头语法=> expr,它其实是{ return expr };的缩写,只适用于一个表达式。方法都有返回值,如果没有指定,那么默认return null最后一句执行。

举个例子,下面的代码表示输出用户输入的参数信息:

1
2
3
void main(List args){
print(args);
}

如何让用户输入信息呢?在Dart中只需进入到该文件,然后使用dart 文件名.dart命令,后面再跟上需要输入的参数即可:

1
2
E:\DartProjects\hellodart\dart_basic\function> dart myfunction.dart 1 "test" True
[1, test, True]

再来看一个有返回值的参数:

1
2
3
4
5
6
7
8
9
void main(){
print(getPersonInfo("张三", 25));
}

String getPersonInfo(String username,int age){
return "name=$username,age=$age";
}

//name=张三,age=25

再来看一下有返回值的情况以及使当方法体只有一个表达式时可以使用的箭头语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
print(getPersonInfo("张三", 25)); //name=张三,age=25
print(getPersonInfo2("李四", 26)); //name=李四,age=26
print(getPersonInfo3("王五", 27)); //null,因为该方法没有返回值
}

String getPersonInfo(String username,int age){
return "name=$username,age=$age";
}

getPersonInfo2(username,age) => "name=$username,age=$age";

getPersonInfo3(String username,int age){
print("name=$username,age=$age"); //name=王五,age=27
}

请注意方法的返回类型和参数的类型都是可以省略的,因此上述getPersonInfo3方法可修改为如下所示:

1
2
3
getPersonInfo3(username,age){
print("name=$username,age=$age"); //name=王五,age=27
}

请注意,如果方法的返回值不是void,那么请不要省略方法的返回类型。

由于getPersonInfo3方法中只有一个表达式,因此也可以使用箭头语法:

1
getPersonInfo3(username,age) =>print("name=$username,age=$age");

可选参数

可选参数有两种,一种是可选命名参数{param1,param2};另一种则是可选位置参数[param1,param2]

可选命名参数

可选命名参数,即使用{}括起来,表明是可选命名参数,如{param1,param2}。可选命名参数在源码中使用较多。

举个例子,假设printPersonInfo方法总共传入了三个参数,其中name是必须传入的参数,而age和gender参数可以不传,此时代码如下:

1
2
3
4
5
6
7
void main(){
printPersonInfo("张三"); //username=张三, age=null, gender=null
}

printPersonInfo(String username,{int? age,String? gender}){
print("username=$username, age=$age, gender=$gender");
}

这里我们在int和String后面添加了一个问号,表示使用对应的可空类型。然后我们尝试依次传入对应参数,方法和执行结果如下:

1
2
3
4
5
6
7
8
9
void main(){
printPersonInfo("张三"); //username=张三, age=null, gender=null
printPersonInfo("李四",age: 26); //username=李四, age=26, gender=null
printPersonInfo("王五",age: 28,gender: "男"); //username=王五, age=28, gender=男
}

printPersonInfo(String username,{int? age,String? gender}){
print("username=$username, age=$age, gender=$gender");
}

由于此处采用的是可选命名参数,因此方法中参数的传入顺序与方法中定义的顺序可以不一致,因为它通过名称来定位参数。

1
2
printPersonInfo("张三",age: 26,gender: "男");  //username=张三, age=26, gender=男
printPersonInfo("张三",gender: "男",age: 26); //username=张三, age=26, gender=男
可选位置参数

可选位置参数,即使用[]括起来,表明是可选位置参数,如[param1,param2]。举个例子,假设printPersonInfo方法总共传入了三个参数,其中name是必须传入的参数,而age和gender参数可以不传,此时代码如下:

1
2
3
4
5
6
void main(){
}

printPersonInfo2(String username,[int? age,String? gender]){
print("username=$username, age=$age, gender=$gender");
}

然后我们尝试依次传入对应参数,方法和执行结果如下:

1
2
3
4
5
6
7
8
9
void main(){
printPersonInfo2("李四"); //username=李四, age=null, gender=null
printPersonInfo2("李四",28); //username=李四, age=28, gender=null
printPersonInfo2("李四",28,"男"); //username=李四, age=28, gender=男
}

printPersonInfo2(String username,[int? age,String? gender]){
print("username=$username, age=$age, gender=$gender");
}

由于此处采用的是可选位置参数,因此方法中参数的传入顺序与方法中定义的顺序必须保持一致,因为它通过参数在参数列表中的位置来定位参数。

1
2
printPersonInfo2("李四",28,"男");  //username=李四, age=28, gender=男
printPersonInfo2("李四","男",28); //这是错误的调用方式

请注意,如果存在具体参数,那么可选参数必须在具体参数之后声明。

1
2
3
4
5
6
7
printPersonInfo(String username,{int? age,String? gender}){
print("username=$username, age=$age, gender=$gender");
}

printPersonInfo2(String username,[int? age,String? gender]){
print("username=$username, age=$age, gender=$gender");
}

也就是说可选参数(无论是命名参数还是位置参数),都必须在具体参数之后声明,否则程序会抛异常。

默认参数值

开发者可以给可选参数指定默认值,这样如果可选参数不传入值,则使用该默认值。如果传入值,则使用传入值取代默认值。通常默认参数的值为null。注意默认值只能是编译时的常量。

举个例子,printUserInfo方法总共传入了三个参数,其中username是必须传入的参数,而age和gender参数可以不传,同时我们设置了age默认值为30,gender默认值为女,这样其实这两个可选参数就是非空类型,所以类型后面就不用添加问号了,此时代码如下:

1
2
3
4
5
6
void main(){
}

printUserInfo(String username,{int age =30,String gender = "女"}){
print("username=$username, age=$age, gender=$gender");
}

然后我们尝试依次传入对应参数,方法和执行结果如下:

1
2
3
4
5
6
7
8
9
void main(){
printUserInfo("张三"); //username=张三, age=30, gender=女
printUserInfo("张三",age: 26); //username=张三, age=26, gender=女
printUserInfo("张三",age: 26,gender: "男"); //username=张三, age=26, gender=男
}

printUserInfo(String username,{int age =30,String gender = "女"}){
print("username=$username, age=$age, gender=$gender");
}

函数返回值

在Dart语言中,函数返回值有如下特点:
(1)所有的函数都有返回值;
(2)如果没有指定函数的返回值,则默认返回值为null;
(3)没有返回值的函数,系统会在最后添加隐式的return语句。

方法对象

前面说过Dart是一个面向对象的语言,因此方法也是对象,属于Function对象。方法可以作为对象赋值给其他变量,也可以作为参数传递给其他方法。

下面的例子就演示了方法作为对象赋值给其他变量:

1
2
3
4
5
6
7
8
void main(){
var func = printHello;
func(); //hello,world
}

void printHello(){
print("hello,world");
}

当然了,由于方法也是对象,属于Function类型,因此上述的func变量也可以改成如下形式:

1
Function func = printHello;

下面再来看一下例子,这个例子说明了方法也可以作为参数传递给其他方法:

1
2
3
4
void main(){
var list = [1,2,3,4];
list.forEach(print);
}

这个list的forEach方法源码如下:

1
2
3
void forEach(void action(E element)) {
for (E element in this) action(element);
}

再来看一个例子,现在有一个整数类型的列表,我们希望它所有元素都变成原来的2倍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
var list = [1,3,5,7];
print(getNewList(list, transferTwice)); //[2, 6, 10, 14]
}

List getNewList(List list,int transferTwice(int num)){
for(var i = 0;i< list.length;i++){
list[i] = transferTwice(list[i]);
}
return list;
}

int transferTwice(int number){
return number*2;
}

匿名方法

匿名方法其实就是没有名字的方法,定义格式如下:

1
2
3
4
(参数1,参数2,...){
方法体...
return 返回值
}

匿名方法的特性有两点:
(1)可赋值给变量,通过变量来进行调用;
(2)可在其他方法中直接调用或者传递给其他方法。

匿名方法赋值给变量,通过变量来进行调用

下面的例子是没有参数的匿名方法:

1
2
3
4
5
6
7
void main(){
var func = (){
print("hello"); //hello
};

func();
}

再来一个具有参数的匿名方法:

1
2
3
4
5
6
7
void main(){
var func = (name){
print("hello,$name"); //hello,world
};

func("world");
}

再来看一个比较有意思的语法()(),即我们可以将匿名方法传入第一个括号中,然后通过第二个括号来调用这个方法:

1
2
3
4
((){
print("hello");
}
)();

但是不建议大家这样使用,这样会极大的降低代码的可读性,增加维护的难度。

匿名方法可在其他方法中直接调用或者传递给其他方法

还记得之前我们在方法对象中的一个例子,将整型列表中的所有元素都变成原来的2倍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
var list = [1,3,5,7];
print(getNewList(list, transferTwice)); //[2, 6, 10, 14]
}

List getNewList(List list,int transferTwice(int num)){
for(var i = 0;i< list.length;i++){
list[i] = transferTwice(list[i]);
}
return list;
}

int transferTwice(int number){
return number*2;
}

现在我们尝试使用匿名方法来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
void main(){
var list2 = [1,3,5,7];
print(getNewList(list2));
}


List getNewList(List list){
var func = (int num){return num*2;};
for(var i = 0;i< list.length;i++){
list[i] = func(list[i]);
}
return list;
}

闭包

闭包是一个方法(对象),它定义在其他方法内部,能够访问外部方法内的局部变量,并持有其状态。即通过闭包可以访问其他方法内定义的局部变量。

举个例子,如下所示就是闭包的一个经典例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var func = a();
func();
func();
func();
}

Function a(){
int count = 0;

printCount(){
print(count++);
}

return printCount;
}

运行一下上述方法,执行结果如下所示:

1
2
3
0
1
2

当然了,闭包也可以使用匿名函数来实现,使用匿名函数作为闭包是比较常用的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main(){
var f = b();
f();
f();
f();
}

Function b(){
int count = 0;
return (){
print(count++);
return;
};
}

面向对象

类与对象

(1)使用关键字class声明一个类;(2)使用关键字new创建一个对象,new可以省略;(3)所有的类都继承自Object类。

声明一个类和创建一个对象的示例代码如下:

1
2
3
4
5
6
7
void main(){
var person = Person(); // 等同于 var person = new Person();
}

class Person{

}

属性和方法

(1)属性(成员变量)就是类中定义的变量,注意类中所有的属性都会隐式的定义setter方法(final关键字修饰的属性除外,它只有getter方法),针对非空的属性会额外定义getter方法。
(2)注意属性的getter和setter方法都是默认生成的。
(3)属性和方法都是通过.来访问的,同时方法不能被重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
var person = Person(); // 等同于 var person = new Person();

person.info();
}

class Person{
String name = "";
int age = 18;
String? nickName;

void info(){
print("name=$name,age=$age,nickName=$nickName");
}
}

请注意在Dart2中,如果某个变量为非空类型,那么它必须初始化,如果变量为空类型,那么可以不进行初始化,且默认值就是null。因此上述方法执行结果如下所示:

1
name=,age=18,nickName=null

由于final关键字修饰的属性是只读的,因此开发者不能对其进行修改。再次强调,Dart中的方法不能被重载

类及成员可见性

(1)Dart中的可见性是以library(库)为单位;
(2)默认情况下,每一个Dart文件就是一个库;
(3)使用_表示库的私有性;
(4)使用import关键字来导入库。

举个例子,我们将之前的Person类抽离出来,放到与main方法同级的包中,之后发现main方法是找不到这个Person对象的:

解决办法就是在main方法中通过import语句导入该Persoon类import 'Person.dart';

再来看个例子,我们在Person类中的name属性前面加上一个_,则表示此属性为私有属性:

1
2
3
4
5
6
7
8
9
10
11
class Person{
String _name = "";
int age = 18;
String? nickName;

String address = "";

void info(){
print("name=$_name,age=$age,nickName=$nickName");
}
}

这就意味着系统就不会给这个name属性添加setter和getter方法,所以你在main方法中获取和修改name属性的值就会抛异常:

计算属性

计算属性,顾名思义就是它的值是通过计算得到的,本身不会存储值。计算属性赋值,其实就是通过计算转换得到其他实例变量。

下面是一个使用对象属性的方式来计算矩形面积的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var rectangle = Rectangle();
rectangle.width = 10;
rectangle.height = 20;

print(rectangle.area()); //200
}

class Rectangle{
num width = 0;
num height = 0;

num area(){
return width * height;
}
}

由于面积会随着长和宽的变化而发生变动,因此可以将这个面积定义为一个计算属性,代码修改为如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var rectangle = Rectangle();
rectangle.width = 10;
rectangle.height = 20;

print(rectangle.area); //200
}

class Rectangle{
num width = 0;
num height = 0;

num get area{
return width * height;
}
}

可以看到计算属性的定义与方法不同,它没有参数列表(),同时在返回类型与方法名称之间有一个get关键字,最重要的是由于它是一个属性,因此在调用的时候不能携带()

由于area属性只有一个表达式,因此可以将其进行简化为如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main(){
var rectangle = Rectangle();
rectangle.width = 10;
rectangle.height = 20;

print(rectangle.area);
}

class Rectangle{
num width = 0;
num height = 0;

num get area => width * height;
}

同时我们还可以给计算属性赋值值,注意里面只能传入一个参数,因此这里尝试将面积除以20来得到它的宽度,根据面积同时求宽和高是不可能的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void main(){
var rectangle = Rectangle();
rectangle.width = 10;
rectangle.height = 20;

rectangle.area = 200;
print(rectangle.width); //10

}

class Rectangle{
num width = 0;
num height = 0;

num get area => width * height;
void set area (num value){
width = value /20;
}
}

构造方法

常规构造方法

构造方法就是在创建对象时被调用的方法,和其他方法所不同的是,构造方法的名称就是类名。

如果没有自定义构造方法,那么会有一个默认的构造方法。如果存在自定义的构造方法,那么默认的构造方法无效。构造方法不能重载。

举个例子,下面的例子中就给User类添加了一个无参数的构造方法,并在函数中给User类的两个成员变量初始化了值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var user = User();
user.name = "张三";
user.age = 26;
}

class User{
late String name;
late int age;

late final String gender;

User(){

}
}

请注意,只有类中没有自定义构造方法,那么系统才会有一个默认的构造方法。如果存在自定义的构造方法,那么默认的构造方法就不会提供(开发者也不能手动提供),这样就不会与方法不允许重载相悖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var user = User("李四",28);
}

class User{
late String name;
late int age;

late final String gender;

//有两个参数的构造方法
User(String name,int age){
this.name = name;
this.age = age;
}
}

由于这种携带参数的构造方法使用较为频繁,于是Dart语言对此提供了语法糖,可将如下代码:

1
2
3
4
User(String name,int age){
this.name = name;
this.age = age;
}

简化为如下,即直接将传入的参数赋值给类对应的属性:

1
User(this.name,this.age);

这个方法后面也可以跟语句:

1
2
3
User(this.name,this.age){
print("hello,world");
}

我们来看一下完整的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
void main(){
var user = User("李四",28);
print(user.age);
}

class User{
late String name;
late int age;

User(this.name,this.age){
print("hello,$name");
}
}

执行一下main方法,可以看到输出结果如下所示:

1
2
hello,李四
28

原因在于使用语法糖的形式给属性赋值(this.name,this.age),它其实是在构造方法执行之前执行,这也是使用语法糖和直接定义构造方法的区别。举个例子,我们尝试直接在构造方法中给一个final类型变量赋值(注意gender前面没有使用late关键字),这是不允许的:

1
2
3
4
5
6
7
8
9
10
11
12
class User{
late String name;
late int age;

final String gender;

User(String name,int age,String gender){
this.name = name;
this.age = age;
this.gender = gender; //错误的方式
}
}

但是在语法糖中我们却是可以对gender变量进行赋值,原因在于final属性的赋值需要在构造方法执行之前执行,而语法糖中的属性赋值就是在构造方法执行之前执行,所以程序就可以运行:

1
User(this.name,this.age,this.gender);

命名构造方法

如果开发者想实现构造方法的重载,可以使用命名构造方法,即采用类名.方法的形式来实现。

如下所示的例子就使用了两个构造方法,其中第一个是常规有参构造,第二个则是命名构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void main(){
var user = User("李四",28,"男");
var user2 = User.withName("张三");
var user3 = User.withAge(30);
var user4 = User.withGender("女");

print(user.age); //28
print(user2.name); //张三
print(user3.age); //30
print(user4.gender); //女
}

class User{
late String name;
late int age;

late final String gender;

User(this.name,this.age,this.gender);

User.withName(String name){
this.name = name;
}

User.withAge(int age){
this.age = age;
}

User.withGender(String gender){
this.gender = gender;
}
}

常量构造方法

(1)如果类是不可变状态,那么可以将对象定义为编译时常量。
(2)使用const关键字来声明构造方法,并且所有属性都是final修饰。
(3)使用const关键字来声明对象,const可以省略。

如下所示的例子,就很好的说明了常量构造方法的三个特点:

1
2
3
4
5
6
7
8
9
10
11
12
13
void main(){
const person = Person("李四",28,"男"); //等同于 const person = const Person("李四",28,"男");
person.name = ""; //程序会抛异常,被final修饰的变量无法被修改
}

class Person{
final String name;
final int age ;

final String gender;

const Person(this.name,this.age,this.gender);
}

工厂构造方法

(1)工厂构造方法类似于设计模式中的工厂模式;
(2)在构造方法前面添加一个factory关键字,就能实现一个工厂构造方法;
(3)在工厂构造方法中可以返回对象。

如下所示的例子,就很好的说明了工厂构造方法的三个特点:

可以看到命名构造方法是不能返回值,而工厂构造方法是可以返回对象的。正确代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main(){
var book = Book("三国演义");
print(book.name);
}

class Book{
final String name;

factory Book(String name){
print("the dart");
return Book._internal(name);
}

Book._internal(this.name){
print("hello,world");
}
}

上述代码执行结果如下所示:

1
2
3
the dart
hello,world
三国演义

构造方法初始化列表

前面我们说过final属性的赋值需要在构造方法执行之前执行,除了前面所说的通过语法糖可以给final类型赋值,还可以通过构造方法初始化列表来实现,因为它也是在构造方法执行之前执行。

构造方法初始化列表使用逗号来分隔初始化表达式,且常用于设置final关键字修饰的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
var person = Person.withMap(Map.fromIterables(["name","age"], ["张三",18]));
print(person.name); //张三
}

class Person{
final String name;
final int age;

//通过语法糖实现final类型赋值在构造方法之前执行
Person(this.name, this.age);

//通过构造函数初始化列表实现final类型赋值在构造方法之前执行
Person.withMap(Map map) : name= map["name"],age= map["age"]{}
}

其实这个构造方法初始化列表就是使用常规或者命名构造方法,通过传入的参数来给final类型的属性赋值,且这个过程会在构造方法执行之前执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void main(){
var person = Person.withMap(Map.fromIterables(["name","age"], ["张三",18]));
print(person.name); //张三

var person2 = Person.withParam("王五", 20);
print(person2.age); //20
}

class Person{
final String name;
final int age;

//通过语法糖实现final类型赋值在构造方法之前执行
// Person(this.name, this.age);

//通过构造函数初始化列表实现final类型赋值在构造方法之前执行
Person.withMap(Map map) : name= map["name"],age= map["age"]{}

Person.withParam(String name,int age) : name=name,age=age;
}

静态成员

(1)可以使用static关键字来实现类级别的变量和方法;
(2)在同一个类中,静态成员不能访问非静态成员,但是非静态成员可以访问静态成员;
(3)类中的常量需要使用static const关键字来修饰。

如下例子就在Page类中定义了一个静态变量currentPage和静态方法prevPage,但是请注意,main方法中创建的page对象无法访问这两个静态成员,这两个静态成员只能通过类名来访问,这一点与Java不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void main(){
var page = Page();
page.nextPage();

//在外部类中,只能通过类名来访问静态成员
Page.currentPage;
Page.prevPage();
}

class Page{
//在类中定义常量必须使用static const来定义
static const int maxPage = 100;

static int currentPage =10;

//上一页
static void prevPage(){
currentPage--;
print("上一页...");
}

//下一页
void nextPage(){
currentPage++;
print("下一页...");
}
}

对象操作符

类型转换as

下面有一个例子,定义了一个Person类,然后在main方法中定义了person变量,首先给person变量赋值为空字符串,之后赋值给Person对象,这样这个person变量的类型其实就是Dynamic类型,所以开发者无法调用Person类型的info方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main(){
var person;
person = "";
person = Person();
}

class Person{
String? name;
int? age;

void info(){
print("name=$name,age=$age");
}
}

此时就可以使用类型转换as运算符了,只需在mian方法中添加如下代码:

1
(person as Person).info();

注意这样使用的前提是这个person对象曾经被赋值过Person类型的值,否则是无法这样使用的。

是否指定类型is、is!

开发者可以使用is或者is!来判断对象是否为某种类型,格式为object is/is! type,这个一般在条件判断中使用较多,根据不同的条件调用对应的方法。

级联操作..

下面所示的是一般我们给某个对象赋值的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main(){
var person = new Person();
person.name = "张三";
person.age = 20;
}

class Person{
String? name;
int? age;

void info(){
print("name=$name,age=$age");
}
}

在main方法中可以使用级联操作来直接给对象赋值,..返回的是当前对象,因此就可以像调用链一样给对象赋值。此时main方法代码可由如下:

1
2
3
4
5
6
void main(){
var person = new Person();
person.name = "张三";
person.age = 20;
person.info();
}

替换为下面的级联操作,这种在日常开发中使用非常频繁:

1
2
3
void main(){
new Person()..name = "张三"..age = 20..info();
}

对象call方法

前面说过Dart中的方法可作为对象来使用的,实际上反过来,对象也可以作为方法来使用。

如果某个类实现了call方法,那么该类的对象就可以作为方法来进行使用。

举个例子,下面的代码就是给Person类创建一个对象,然后给这个对象赋值属性并调用info方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){
var person = Person();
person.name = "张三";
person.age = 20;

person.info();
}

class Person{
String? name;
int? age;

void info(){
print("name=$name,age=$age");
}
}

现在我们尝试实现一个call方法,那么此时这个person对象就是一个方法了,我们就可以像调用方法一样调用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main(){
var person = Person();
person.name = "张三";
person.age = 20;

person(); //类实现了call方法,那么这个类的实例对象,就能像方法一样被调用执行了

}

class Person{
String? name;
int? age;

void call(){
print("name=$name,age=$age");
}
}

上述main方法执行结果如下所示:

1
name=张三,age=20

这call方法比较特殊,只需要名称为call即可,对于参数列表和返回值类型不做任何要求。下面的例子就是call方法携带参数且有返回值的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(){
var person = Person();
String result = person("李四",28);
print(result); //name=李四,age=28

}

class Person{
String? name;
int? age;

String call(String name,int age){
return "name=$name,age=$age";
}
}

不过这种方式不建议使用,毕竟对象和方法还是有区别。