首页 > 其他分享 >【Kotlin】函数

【Kotlin】函数

时间:2024-04-06 11:00:36浏览次数:21  
标签:Int 函数 myFun Kotlin println fun main Data

1 常规函数

1.1 无参函数

fun main() {
    myFun()
}

fun myFun() {
    println("myFun") // 打印: myFun
}

1.2 有参函数

1)常规调用

fun main() {
    myFun("myFun") // 打印: myFun
}

fun myFun(str: String) {
    println(str)
}

2)形参指定默认值

fun main() {
    myFun() // 打印: abc
}

fun myFun(str: String = "abc") {
    println(str)
}

3)实参指定变量名

fun main() {
    myFun(b = 123, a = "abc") // 打印: abc123
}

fun myFun(a: String, b: Int) {
    println(a + b)
}

1.3 有返回值函数

1)常规调用

fun main() {
    var c = add(3, 5)
    println(c) // 打印: 8
}

fun add(a: Int, b: Int): Int {
    return a + b
}

​ 说明:对于无返回值类型函数,其返回类型为 Unit,如下,也可以省略不写。

fun myFun(str: String): Unit {
    println(str)
}

2)单行函数体简化

​ 当函数内部只有一行代码时,可以简化如下。

fun main() {
    var c = add(3, 5)
    println(c) // 打印: 8
}

fun add(a: Int, b: Int): Int = a + b

1.4 可变长参数函数(vararg)

1)常规调用

fun main() {
    myFun("aa", "bb", "cc") // 打印: aa、bb、cc
}

fun myFun(vararg parms: String) {
    for (str in parms) {
        println(str)
    }
}

​ 说明:函数的可变长参数个数最多为 1 个。

2)使用数组接收可变长参数

fun main() {
    myFun("aa", "bb", "cc") // 打印: 3
}

fun myFun(vararg parms: String) {
    var arr: Array<out String> = parms
    println(arr.size)
}

3)将数组传给可变长参数函数

fun main() {
    var arr: Array<String> = arrayOf("aa", "bb", "cc")
    myFun(*arr)  // 打印: 3
    myFun("xx", *arr, "yy")  // 打印: 5
}

fun myFun(vararg parms: String) {
    println(parms.size)
}

2 函数类型变量

2.1 函数类型变量

1)无参函数变量

fun test() {
    println("test")
}

fun main() {
    var myFun: () -> Unit = ::test
    myFun() // 打印: test
}

2)有参函数变量

fun test(a: Int, b: String): Unit {
    println("test, $a, $b")
}

fun main() {
    var myFun: (Int, String) -> Unit = ::test
    myFun(123, "abc") // 打印: test, 123, abc
}

3)有返回值函数变量

fun test(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    var myFun: (Int, Int) -> Int = ::test
    println(myFun(3, 5)) // 打印: 8
}

2.2 匿名函数

​ 匿名函数即没有名字的函数,在声明函数变量时,可以指向一个匿名函数。

fun main() {
    var myFun: (Int, Int) -> Int = fun(a: Int, b: Int): Int {
        return a + b
    }
    println(myFun(3, 5)) // 打印: 8
}

​ 可以使用 Lambda 表达式简化如下。

fun main() {
    var myFun: (Int, Int) -> Int = { a, b ->
        a + b
    }
    println(myFun(3, 5)) // 打印: 8
}

3 内联函数(inline)

​ 内联函数是使用 inline 关键字修饰的函数,编译后会自动将函数体内的代码复制到调用处,以优化代码执行效率。

3.1 常规内联函数

​ Test.kt

fun main() {
    myFun()
}

inline fun myFun() {
    println("内联函数")
}

​ 以上代码经过编译运行后,依次点击【Tools → Kotlin → Show Kotlin Bytecode】,生成字节码文件。

img

​ 再点击 DeCompile 按钮反编译字节码文件,会生成对应的 Java 文件。

img

public final class TestKt {
   public static final void main() {
      String var1 = "内联函数";
      System.out.println(var1);
   }

   public static void main(String[] var0) {
      main();
   }

   public static final void myFun() {
      String var1 = "内联函数";
      System.out.println(var1);
   }
}

​ 说明:可以看到 myFun 函数里的代码被复制到调用处了。

3.2 带 return 的嵌套内联函数

1)return 不带标签

fun main() {
    outFun {
        println("inFun")
        return // 等价于: return@main
    }
    println("main end") // 未打印
}

inline fun outFun(inFun: () -> Unit) {
    inFun()
    println("outFun end") // 未打印
}

​ 运行结果如下。

inFun

​ "outFun end" 和 "main end" 未打印,这是因为内联函数会直接将 return 语句复制到 main 函数中。

2)return@标签

fun main() {
    outFun {
        println("inFun")
        return@outFun
    }
    println("main end")
}

inline fun outFun(inFun: () -> Unit) {
    inFun()
    println("outFun end")
}

​ 运行结果如下。

inFun
outFun end
main end

4 泛型函数

​ 泛型的类型检查只存在于编译阶段,在源代码编译之后,不会保留任何关于泛型类型的内容,即类型擦除。

4.1 简单泛型函数

1)单泛型参数

fun main() {
    myFun(123)  // 打印: 123
    myFun("abc")  // 打印: abc
    myFun(true)  // 打印: true
    myFun(null)  // 打印: null
}

fun <T> myFun(param: T) {
    println(param)
}

2)多泛型参数

fun main() {
    var res: Boolean = myFun("abc", 123, true) // 打印: abc, 123
    println(res) // 打印: true
}

fun <R, T, S> myFun(a: T, b: S, c: R): R {
    println("$a, $b")
    return c
}

4.2 类中泛型函数

fun main() {
    var c1: MyClass<String> = MyClass()
    c1.myFun("abc") // 打印: abc
    var c2: MyClass<Int> = MyClass()
    c2.myFun(123) // 打印: 123
}

class MyClass<T> {
    fun myFun(a: T) {
        println(a)
    }
}

4.3 自动推断泛型类型

​ Kotlin 提供了下划线(_)运算符可以自动推断类型。

fun main() {
    myFun<Int, _>()
}

fun <S : Comparable<T>, T> myFun() {
    println("test _")
}

​ Int 类和 Comparable 类的定义如下。由于 Int 继承了 Comparable,因此会自动推断 "_" 为 Int。

public interface Comparable<in T>
public class Int private constructor() : Number(), Comparable<Int>

4.4 抗变、协变和逆变

1)抗变

​ 如下,Int 是 Number 的子类,Number 引用可以指向 Int 对象,但是 Data 引用不能指向 Data 对象,Data 引用也不能指向 Data 对象,该现象称为抗变。

img

2)协变

​ 通过 out 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? extends Number"。

class Data<T>(var value: T)

fun main() {
    var data1: Data<Int> = Data<Int>(1)
    var data2: Data<out Number> = data1
    println(data2.value) // 打印: 1
    // data2.value = 1 // 编译错误, setter方法被限制
}

​ 说明:协变后,不能修改协变元素。使用 out 修饰的泛型不能用作函数的参数,对应类型的成员变量 setter 方法会被限制,只能当做一个生产者使用。

3)逆变

​ 通过 in 关键字表示 Data 引用能指向 Data 对象,类似于 java 中的 "? super Int"。

class Data<T>(var value: T)

fun main() {
    var data1: Data<Number> = Data<Number>(1f)
    var data2: Data<in Int> = data1
    println(data2.value) // 打印: 1.0
    data2.value = 2
    var a: Any ?= data2.value // 只能用Any接收value
}

​ 说明:逆变后,只能使用 Any 接收逆变元素。使用 in 修饰的泛型不能用作函数的返回值,对应类型的成员变量 getter 方法会被限制,只能当做一个消费者使用。

​ **4)通配 **

​ 在有些时候,我们可能并不在乎到底使用哪一个类型,我们希望一个变量可以接受任意类型的结果,而不是去定义某一个特定的上界或下界。在Kotlin 泛型中,星号(*)代表了一种特殊的类型投影,可以代表任意类型。

class Data<T>(var value: T)

fun main() {
    var data1: Data<Int> = Data<Int>(1)
    var data2: Data<*> = data1 // Data<*>等价于Data<out Any>
    println(data2.value) // 打印: 1.0
    // data2.value = 2 // 编译错误, setter方法被限制
    var a: Any ?= data2.value // 只能用Any接收value
}

​ 说明:由于不确定具体类型,使用时只能是 Any 类型。

4.5 泛型的界

​ Kotlin 泛型中,可以为其指定上界。

1)单上界

class Data<T: Number>(var value: T)

fun main() {
    var data1: Data<Int> = Data<Int>(1)
    // var data1: Data<String> = Data<String>("abc") // 编译错误, 指定了上界为Number
    var data2: Data<*> = data1 // Data<*>等价于Data<out Number>
    println(data2.value) // 打印: 1.0
    // data2.value = 2 // 编译错误, setter方法被限制
    var a: Number = data2.value // 可以用Number接收value
}

2)多上界

open class A {}
interface B {}

class Data<T>(var value: T) where T: A, T: B

4.6 具化类型参数(reified)

​ Kotlin 的内联(inline)函数可以使用 reified 关键字具化类型参数,允许在函数体内部检测泛型类型,因为这些类型信息会被编译器内嵌在调用点。但是,这只适用于内联函数,因为内联函数中的类型信息在编译时是可知的,并且实际类型会被编译到使用它们的地方。

​ 以下调用会编译报错。

img

​ 通过 inline 和 reified 修饰符,可以解决编译报错问题,如下。

inline fun<reified T> isType(value: Any) : Boolean {
    return value is T
}

fun main() {
    println(isType<Int>("abc")) // 打印: false
    println(isType<String>("abc")) // 打印: true
}

​ 声明:本文转自【Kotlin】函数

标签:Int,函数,myFun,Kotlin,println,fun,main,Data
From: https://www.cnblogs.com/zhyan8/p/18115116

相关文章

  • vue3的ref和reactive函数
    在vue3中需要引入ref和reactive函数对变量进行声明 首先引入ref,reactive函数,vue3不同于vue2的是,变量的声明需要写在setup函数中,(vue2是直接写在data函数中)其中ref主要是对一些基础数据变量声明,如string,number,boolean,而reactive则是对复杂的,入对象进行声明最后,定义......
  • php安全之curl函数
    cURL是一个用于进行各种网络请求的强大工具,但如果未正确验证和限制用户提供的URL,它也可能被用于发起SSRF攻击curl_close()关闭一个cURL会话。curl_copy_handle()复制一个cURL句柄和它的所有选项。curl_errno()返回最后一次的错误号。curl_error()返回一个保护当前会话最近一次......
  • zynq Lwip学习笔记-ip4_input函数
    这里写目录标题前言一、概述二、函数体三、调用关系前言最近在学习zynq中的lwip协议族,找不到很好的记笔记的地方,所以就用csdn记录一下自己的学习过程。现在对lwip不熟悉,只是把官方的lwipechoserver例程跑了一下,能跑通就一点点的照着学了,笔记都是根据自己的理解写的......
  • zynq Lwip学习笔记-low_level_init函数
    这里写目录标题前言一、概述二、函数体三、调用关系前言最近在学习zynq中的lwip协议族,找不到很好的记笔记的地方,所以就用csdn记录一下自己的学习过程。现在对lwip不熟悉,只是把官方的lwipechoserver例程跑了一下,能跑通就一点点的照着学了,笔记都是根据自己的理解写的......
  • zynq Lwip学习笔记-setup_isr 函数
    这里写目录标题前言一、概述二、函数体三、调用关系前言最近在学习zynq中的lwip协议族,找不到很好的记笔记的地方,所以就用csdn记录一下自己的学习过程。现在对lwip不熟悉,只是把官方的lwipechoserver例程跑了一下,能跑通就一点点的照着学了,笔记都是根据自己的理解写的......
  • DwmGetDxSharedSurface函数,可用于窗口后台截图
    ReturnsdetailsforawindowsDirectXsurfaceSyntaxBOOLWINAPIDwmGetDxSharedSurface(    HWNDhwnd,    HANDLE*phSurface,    LUID*pAdapterLuid,    ULONG*pFmtWindow,    ULONG*pPresentFlags,    ULONGLONG*pWin32kUpdateId)......
  • 【C语言学习】之字符数组与字符串处理函数
    1.字符数组1.字符数组的初始化1.单字符形式chara[3]={'a','b','c'}                定义一个字符型一维数组,数组名a,三个下表变量a,b,ccharb[][3]={'a','b','c','d','e','f','g'}  ......
  • 函数式接口
    什么是函数式(Functional)接口只包含一个抽象方法的接口,称为函数式接口你可以通过 Lambda 表达式来创建接口的对象。(若 Lambda 表达式抛出一个受检异常–即:非运行时异常,那么该异常需要在目标接口的抽象方法上进行声明)。我们可以在一个接口上使用 @FunctionalInterface ......
  • Python程序设计 魔法函数
    1.魔法方法Python中有一些特殊方法,它们允许我们的类和Python更好地集成。在标准库参考(StandardLibraryReference)中,它们被称为魔法方法(MagicMethods),是与Python的其他特性无缝集成的基础。例如,我们用字符串来表示一个对象的值。Object 基类包含了__repr__() 和__str__()......
  • Java登陆第三十八天——VUE3生命周期、钩子函数、组件拼接、组件传参(组件通信)
    生命周期之前在Servlet等也学习过生命周期,相同的,VUE组件也有生命周期。VUE组件完全解析到浏览器,会经过如下过程。(简单理解)vue组件被实例化的过程,称为,组件实例化。组件实例解析到浏览器中,称为,挂载。组件实例从浏览器中删除,称为,卸载。钩子函数vue组件解析到浏览器的......