美文网首页dart入门潜修Dart
dart入门潜修基础篇之方法

dart入门潜修基础篇之方法

作者: 寒潇2018 | 来源:发表于2019-01-31 19:02 被阅读0次

    本文收录于dart入门潜修系列教程

    创作不易,转载还请备注。

    方法

    上篇文章我们提到过,在dart中一切都是对象,方法也不例外,方法也是个对象,也就是说dart中的方法是“一等公民”。这意味着我们可以将方法赋值给一个变量,也可以将方法作为参数传入到另一个方法中。实际上,在dart中,方法对应的类型是Function类。先来看下方法的简单例子:

    void main() {
      print("hello world!");
    }
    

    没错,这个就是我们非常熟悉的应用程序的执行入口main方法。从main方法可知,dart中的方法和其他语言一样,有返回值+方法名+方法参数+方法体组成。在方法体中,如果有返回值则需要显示使用return语句进行返回,否则会返回默认值null,如下所示:

    //计算两个整数的和
    int sum(int x, int y) {
      int sum = x + y;//注意,此方法没有返回值
    }
    //计算两个整数的和
    int sum2(int x, int y) {
      int sum = x + y;
      return sum;//显示指定了方法返回值
    }
    //测试
    void main() {
      print(sum(1, 2));//打印null
      print(sum2(1, 2));打印3
    }
    

    上面例子演示了在dart中方法返回值的不同场景,因为在dart中一切即对象,所以除了void类型,其他类型如果不显示指定其返回值,默认都返回null。

    但dart在方法定义的时候可以省略其返回类型,此时将会由return语句决定该方法返回什么类型,如下所示:

    //定义一个方法,没有指定该方法的返回值类型
    sum(int x, int y) {
      return x + y;//该方法的返回类型将由return语句决定,即返回int
    }
    //测试
    void main() {
      print(sum(1, 2));//打印3
    }
    

    如果在定义方法的时候没有指定返回类型,并且方法体也没有调用return语句进行显示返回,那么该方法依然会返回默认值null,而不是void。

    在dart中,还支持箭头语法,即对于方法体中只有一个表达式的方法,我们可以使用箭头语法,如下所示:

    //使用箭头语法
    sum(int x, int y) => x + y;
    //测试
    void main() {
      print(sum(1, 2));//打印3
    }
    

    需要注意的是,箭头语法只能在特定环境下使用,其使用条件必须同时满足以下条件:

    1. 方法体中只有一条语句。
    2. 该语句必须是一个表达式。

    dart中的方法支持可选参数,示例如下:

    //我们定义了一个合并两个字符串的方法combiningStr
    //但是第二个字符串是可选的,如果没有传则返回第一个字符串
    String combiningStr(String firstStr, [String secondStr]) {
      String result =  firstStr;
      if(secondStr != null){
        result += secondStr;
      }
      return result;
    }
    //测试
    void main() {
      print(combiningStr("hello"));//可以省略第二个参数
      print(combiningStr("hello", " world"));//两个参数都传
    }
    

    打印结果如下:

    hello
    hello world
    

    上面例子演示了dart中可选参数的用法,可选参数的语法是使用中括号[ ]包裹即可。

    既然讲到了可选参数,我们再回头看下dart中的程序入口main方法,我们知道类似于java等语言,其main方法都接收入参,用于接收命令行参数,那么dart中如果也有命令行参数该如何处理呢?

    看过可选参数这一部分之后,想必大家已经能想到了,main方法实际上也是有参数的,只不过这个参数是可选的,当有命令参数的时候就会触发,其完整的定义如下所示:

    void main(List<String> args) {
    }
    

    dart还支持命名参数,即在传入参数的时候指定传递给那个参数,依然是上面的例子,我们还可以这么做,如下所示:

    //显示设置入参的flag,即使用{}包括起来
    String combiningStr({String firstStr, String secondStr}) {
      String result = firstStr;
      if (secondStr != null) {
        result += secondStr;
      }
      return result;
    }
    //测试
    void main() {
    //调用的时候我们就可以显示指定flag来指定传参
      print(combiningStr(firstStr: "hello", secondStr: " world"));
    }
    

    此时,如果我们只传入一个参数也是合法的,只不过我们要确保方法体内部对null的处理,以避免crash,比如我们可以这么调用:

    //只显示指定传入secondStr,没有传入firstStr
      print(combiningStr(secondStr: " world"));
    

    注意,命名参数和可选参数无法同时存在。

    从上面的代码可知,如果我们采用命名参数就会存在一个问题,那就是需要处理没有传入参数时的情况。比如,上面我们就可以选择只传secondStr,而不传firstStr,此时如果firstStr是combiningStr方法必须的参数,那么就可能会产生未知的错误,这个是很不好的体验,针对这种情况,dart为我们提供了@required注解,用于标注哪些参数必须要传入,如下所示:

    import 'package:meta/meta.dart';
    //我们使用@required注解来标明firstStr为必传参数
    String combiningStr({ @required String firstStr, String secondStr}) {
      String result = firstStr;
      if (secondStr != null) {
        result += secondStr;
      }
      return result;
    }
    //测试方法
    void main() {
      print(combiningStr(secondStr: " world"));//!!!错误,不能省略参数firstStr
      print(combiningStr(firstStr: "hello", secondStr: " world"));//正确
    }
    

    需要注意的是@required注解并没有在dart标准库中,需要单独安装(位于meta包中),刚好阐述到这一部分,我们就来看下如何在dart工程中引入新包。

    dart工程使用YAML文件来管理包依赖,yaml并不是本篇文章的阐述重点,所以下面直接来说下如何使用。

    首先在我们的工程父目录中新建yaml文件(名字可随意取),然后在yaml文件中引入meta包:

    name: dart_lean
    dependencies:
      meta: ^1.1.7
    

    最后我们在使用的地方import即可,如下所示:

    import 'package:meta/meta.dart';
    

    这样我们就可以使用meta包暴露的接口了。

    同其他语言一样,dart也支持方法参数拥有默认值,可选参数和命名参数都可以定义默认值,如下所示:

    //我们为firstStr指定了默认值“hello"
    String combiningStr({String firstStr = "hello", String secondStr}) {
      String result = firstStr;
      if (secondStr != null) {
        result += secondStr;
      }
      return result;
    }
    //测试方法
    void main() {
      print(combiningStr(secondStr: " world"));//打印 hello world
    }
    

    在dart的旧版本中,支持使用冒号(:)语法来指定默认值,但是新的版本也不建议这么用,而是使用等于号(=) 来替代冒号。

    方法是一等公民

    前面提到了dart中的方法是一等公民,也解释了什么是一等公民,本节我们来看下几个例子,如下所示:

    //我们定义了一个打印整型元素的方法printIntElement
    void printIntElement(int element) {
      print(element);
    }
    //测试
    void main(List<String> args) {
      var list = [1, 2, 3];//生成一个整型列表
      var myPrint = printIntElement;//我们可以将方法赋值给一个变量
      list.forEach(myPrint);//然后将该方法地址传递给forEach方法
    }
    

    上面代码展示了方法作为一等公民的“特权”,对于上面的代码,我们可以做以下总结:

    1. dart中的方法确实如同一般变量一样,可以自由的赋值、传参。
    2. 当方法作为参数传入另一个方法时(姑且称之为A方法),我们可以将A方法称为高阶方法。
    3. 只要接收方法类型作为入参的方法,我们才能将方法实例传入。比如上面的forEach方法,实际上它本身接收的就是个方法类型,其定义如下所示:
    //forEach实际上接收的就是一个返回值为void方法类型
      void forEach(void action(E element)) {}
    

    匿名方法

    匿名方法大家一定非常熟悉了,充斥在各个语言当中。匿名方法,故名思议就是没有名字的方法。其实和匿名方法相提并论的还有lambda表达式、闭包等,关于这些概念我曾在我的另一个系列文章中阐述过,具体可以参见kotlin入门潜修之进阶篇—高阶方法和lambda表达式这篇文章。下面来看下dart中匿名方法,先来看下其定义(同其他语言一样):

    ([[Type] param1[, …]]) { 
      codeBlock; 
    }; 
    

    看上去除了没有方法名字之外,和命名方法一模一样,确实如此,接下来我们看一个匿名方法使用的具体案例,这个案例使用的正是上述我们阐述过的list的forEach方法,即我们不再提供显示的打印方法,而是采用匿名方法实现打印功能,如下所示:

    void main(List<String> args) {
      var list = [1, 2, 3];
    //forEach的入参就是匿名方法
      list.forEach((item){
        print(item);
      });
    }
    

    由于上述匿名方法只有一条语句,我们还可以简化如下:

      list.forEach((item) => print(item));
    

    作用域

    很多语言都有作用域,所谓作用域就是位于其中的代码只能在特定的范围内生效,外界无法访问到,dart同样不例外,也有自己的作用域。来看个例子:

    var topLevelStr = "I am top Level str";//顶层作用域
    void main(List<String> args) {//main方法作用域
      var mainScopeStr = "I am in main function : $topLevelStr";
      void inMain() {//inMain方法作用域
        var inMainScopeStr = "I am in inMain function : $mainScopeStr : $topLevelStr";
        void inInMain() {//inInMain方法作用域
          var inInMainScopeStr = "Iam in inInMain function : $mainScopeStr : $inMainScopeStr : $topLevelStr";
        }
      }
    }
    

    由示例可以看出,里层的作用域可以访问其外层的任何一个作用域,但是外层作用域却无法访问内层的作用域。

    在dart中,这种作用域被称为词法作用域,即变量的书写位置决定了其作用范围。

    闭包

    什么是闭包?在kotlin入门潜修之进阶篇—高阶方法和lambda表达式这篇文章中,我曾从作用域的视角阐述什么是闭包,这里基于上述词法作用域再阐述下。

    首先,闭包是基于词法作用域的,那些可以访问其词法作用域中(包括其外层作用域)的变量的方法对象都可称之为闭包。

    来看个闭包的例子:

    //定义一个接收int入参的方法sum,其返回值是个匿名方法,
    //该匿名方法同样接收一个int型的入参
    sum(int firstNum) {
      return (int secondNum) => firstNum + secondNum;
    }
    //测试
    void main() {
      print(sum(1)(2));//打印'3'
    }
    

    上面代码演示了闭包的使用,在sum方法中,我们返回了一个匿名方法对象,该匿名方法对象使用了其外层的变量,即sum方法的入参firstNum,最终完成两个数的相加

    在这个过程中,sum(1)其实就是一个闭包,由于其返回值就是上面我们提到的那个匿名方法,所以我们还可以继续像调用方法一样,继续调用sum(1),来完成最终的计算,即sum(1)(2)。如果不太明白这种写法,我们完全可以将其拆分出来,表示如下:

      var tempSum = sum(1);//返回一个tempSum方法对象
      print(tempSum(2));//调用该方法
    

    至此,本篇文章阐述完毕。

    相关文章

      网友评论

        本文标题:dart入门潜修基础篇之方法

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