函数

定义

  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 类中定义新的成员扩展。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容