函数

作者: 一江碎月 | 来源:发表于2018-06-10 21:40 被阅读0次

    定义

    1. 使用 fun 关键字定义一个函数。fun 后为函数名,函数名后为参数列表,参数列表后为冒号,冒号后为返回值类型。

      函数格式
    2. 如果函数返回值为 void,则可以省略冒号及返回值类型。

    fun test(a: Int) { // 该函数没有返回值
        println("a = $a")
    }
    
    fun test2(b: Int): String { // 该函数返回值类型为 String
        return "$b"
    }
    

    参数

    1. 不能使用 var 或 val 修饰

    2. 必须指明类型。如:

    fun test(a:Int,b:Int = 3 ) = a+b
    
    fun main(args:Array<String>){
        println(test(2)) //5
        print(test(3,4))  // 7
    }
    

    命名参数

    在调用函数时,可以使用参数名指定为某个参数传值。

    命名参数主要是为了提高代码的可读性 —— 阅读代码时,可以很方便的知道不同的实参是为哪个形参进行赋值。

    1. 参数的顺序可以调换

      fun main(args: Array<String>) {
          println(test(a = 1, c = 2, b = "bb"))
      }
      
      fun test(a: Int, b: String, c: Int) = "a = ${a},b = ${b},c = ${c}"
      
    2. 命名参数的后面所有参数必须是命名参数。如上述代码中,如果改写成如下格式便会出错,因为 b 使用了命令参数,而后面没有使用:

      println(test(1, b = "bb", 2))
      
    3. 命名参数只能在 kt 之间使用,kt 调 java 不支持

    默认参数值

    kt 中,在声明函数时可以为参数指定默认值。当调用者没有传递该参数时,该参数的值为默认值。

    默认参数的主要原因是为了避免太多的重载方法。

    1. 当按常规方法调用函数时,除了最后设置有默认值的参数可以省略,必须为每一个参数进行赋值。可以省略末尾的有默认值的参数,中间的不参省略

      如下示例中,传参时必须为 b 进行赋值,但可以省略 d 的实参。常规调用时,没办法做到只为 a,c 赋值。

      
      fun main(args: Array<String>) {
          println(test(1, "bb",2))
      }
      
      fun test(a: Int, b: String = "bb", c: Int,d:Int=3) = "a = ${a},b = ${b},c = ${c},d=${d}"
      
    2. 命名参数时,可以按任意顺序,省略所有有默认值的参数。如下,在调用时,完全没有考虑顺序,也没有为任何一个有默认值的参数进行传参。

      fun main(args: Array<String>) {
          println(test(c = 2,a = 1))
      }
      
      fun test(a: Int, b: String = "bb", c: Int,d:Int=3) = "a = ${a},b = ${b},c = ${c},d=${d}"
      

    表达式函数体

    当一个函数的函数体中只有一个表达式的时候,可以变成表达式函数。

    所谓的表达式函数指的是:除掉普通函数的大括号以及 return 语句,并使用等号将表达式连接在参数列表后。

    同时,也可以省略返回值类型。

    fun test(b: Int) = "$b"
    fun test2(b:String):String = "$b is b"
    

    在第一个表达式函数中,并没有指定返回值类型。因为函数体只是一个表达式,而表达式的返回值可以经过类型推导得到。


    顶层函数

    kt 中函数可以不依赖于任何类而单独存在。这些函数被称为顶层函数。

    因为 jvm 只能调用类中的方法,因此 kt 被编译后,顶层函数会被编译到一个与文件名相同的类中,并且成为该类中的一个静态函数。

    例如 kotlin 中代码如下:

    // 文件名为 Demo.kt
    package com.demo
    fun test(a:Int,b:String) = "${a}+${b}"
    

    经过编译后,会生成 DemoKt 类,而 test 是该类中的一个静态方法。其调用过程如下:

    import com.demo.DemoKt; // 导入 kt 文件编译后生成的类
    public class Test {
        public static void main(String[] args){
            String s = DemoKt.test(1, "2"); // 调用类中的静态方法
            System.out.println(s);
        }
    }
    

    @file:JvmName

    指定 kt 文件编译后的类名。

    该注解必须使用在文件的第一行,凌驾于 package 之上。如将上述文件修改如下,那么调用时就应该使用 Utils.test ,而不是 DemoKt.test。

    @file:JvmName("Utils")
    package com.demo
    fun test(a:Int,b:String) = "${a}+${b}"
    

    顶层属性

    与顶层函数一样,属性也可以不依赖任何类而单独存在。该定义的属性,与普通的类中的属性一样,java 使用时需要通过 getter / setter 进行处理。

    @file:JvmName("Utils")
    package com.demo
    var count = 0
    val age = 10
    

    Java 使用时,如下:

    Utils.getAge();
    Utils.setCount(1);
    Utils.getCount();
    

    const

    定义常量的顶层属性。

    上述定义的顶层属性,都会生成 getter / setter 方法以供 java 调用,并不是 java 中的 public static final 。如果使用 const 修饰的变量转换成 Java 后,就是 public static final。如:

    @file:JvmName("Utils")
    package com.demo
    const val score = 10;
    

    外界调用时,可以直接使用 Utils.score,该值不能被修改。

    kt 中无法定义出被 public static 修饰的属性。


    可变参数

    1. 使用 vararg 修饰符修饰

    2. 可变参数可定义在任意位置

    3. java 中可以将一个数组直接赋值给一个可变参数,但 kt 中数组必须使用 * 进行解包

    4. 可变参数后面的形参进行赋值时,必须使用命名参数 —— 不然无法识别该实参是给可变参数的,还是给别的形参的,即使类型不同。

    // 可变参数可以定义在任意位置
    fun test(vararg a: Int, b: Int) {
        val sb = StringBuilder()
        for (index in 0..(a.size - 1)) {
            sb.append("${a[index]} ")
        }
        sb.append(b)
        println(sb.toString())
    }
    
    fun main(args: Array<String>) {
        val a: IntArray = intArrayOf(1, 2, 3, 4)
        test(*a, b = 5) // 数组必须被解包
    }
    

    中缀调用

    使用 infix 关键字修改的函数为中缀函数。

    调用中缀函数时可以省略点号和括号,而且中缀函数需要满足以下条件:

    • 必须是成员函数或扩展函数。因为这样才能确定调用者的类型。

    • 必须只有单个参数。

    • 参数不能是可变参数,而且不能有默认值

    • 接受者和参数都需要明确指定。即使接受者是 this ,也不能省略。

    // 扩展函数的中缀
    infix fun Any.test(a: Int) = println("${a * 2}")
    
    class My {
        // Any 为所有 kt 类的父类,该函数表示任意一个对象都可以调用 add 函数。
        infix fun Any.add(s: String) = "${this}+${s}"
    
        fun test(s: String) = this add s
        // test2 与 test 效果一样
        fun test2(s:String) = this.add(s)
    }
    
    fun main(args: Array<String>) {
        val my = My()
        println(my.test("xxx"))
        println(my.test2("xxx"))
    }
    

    注意其中的 test 与 test2 两个函数,都没有省略 this。这是中缀函数要求的:不能省略 this。


    局部函数

    kt 中可以在函数中嵌套函数。

    1. 局部函数可以访问所在函数的所有参数和变量,但只能是 定义在局部函数前面的参数和变量

      fun test(){
          val b = "name"
          fun name() = println("${b}") // 局部函数
      
          val a = "age" // name 函数无法访问到该变量
          name()
      }
      
    2. 局部函数可以定义在扩展函数中

      fun User.validate() {
          fun vali(name:String,value:String){
              if (value.isEmpty()) {
                  throw IllegalArgumentException("${name} 不能为空")
              }else{
                  println("正确的")
              }
          }
          vali("id",this.id)
      }
      
      fun main(args: Array<String>) {
          val u1 = User("","11")
          val u2 = User("2","22")
          u2.validate()
          u1.validate() // 该句报错
      }
      

    可空类的扩展

    扩展函数的接收者类型可以为可空类型

    定义成可空类型时,扩展函数中使用 this 可能为 null,所以需要单独判断。

    fun main(args:Array<String>){
        var t:Test? = Test()
        t.test()
        t = null
        t.test()
    }
    
    class Test
    // 类型使用 Test?,表示该方法可以被可空类型的对象调用
    fun Test?.test() {
        if (this == null) println("xxx") else println("$this")
    }
    

    成员扩展

    在一个类中为另一个类定义扩展函数或属性,这样的函数或属性叫成员扩展

    1. 成员扩展不能在类的作用域之外使用

      fun main(args: Array<String>) {
          Outer().test("12345678")    
        //  println("aabbcc".six())  在类的外面无法访问在类中定义的扩展函数 six()
      }
      
      class Outer {
          fun String.six(): Char = toCharArray()[6]
          fun test(s: String) {
              println(s.six())
          }
      }
      
    2. 无法像普通的扩展函数那样,在不改变类的情况下添加新的扩展。如上例,在不改变 Outer 类的情况下,无法为 String 在 Outer 类中定义新的成员扩展。

    相关文章

      网友评论

        本文标题:函数

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