kotlin号称更好的java,不仅支持java的绝大部分语法,还新增了非常多语言特性。函数作为编程语言最重要的核心(我认为没有之一),kotlin的函数对于像我这样的初学者来说“面目狰狞”,本文记录了我学习过程中遇到的各种与函数有关的概念,并对各自的原理做一点点探索。
本文涉及概念:顶层函数、成员函数、抽象函数、lambda函数、高阶函数、嵌套函数、内联函数
剩余概念准备放到下一篇:扩展函数、匿名函数、标准函数、构造函数、委托函数、覆盖函数、挂起函数、泛型函数、回调函数
(如果全都很熟悉就别来csdn摸鱼了,你已经是大佬了,赶紧撸代码吧)
函数的概念
几乎所有编程语言都有函数,古老的x86汇编都支持LABEL-RET的结构实现函数。那么如何给函数下定义呢,这太难了,而且也没必要。因为不同的语言函数之间的语法相差甚远,但其思想万变不离其宗。所以借用java中的概念,函数就是一段可能被多次执行的代码块。
在kotlin和java中,函数更多被称为方法(function),这只是一种称呼上的差异,选择你喜欢的即可。kotlin中基本函数语法如下
fun demoOne(x: Int): String {
val res = x.toString()
return res
}
和其他语言别无两样,无非是函数关键字(fun)、函数名、参数列表、参数类型、返回类型、函数体、返回语句。kotlin在这些概念的基础上增加了非常多的语言特性,在下文中会一一涉及到。
但有一个比较特殊的点需要先注意下,kotlin不能返回null,而是返回Unit。表示该函数返回一个没有意义的值。注意是没有意义,不代表没有返回值。事实上Unit是一种类型。其源码如下。Unit用法和java的void相似,但原理不同。
public object Unit {
//一个全局的单例,只实现了toString方法
//其父类是Any,是kotlin中一切类的根结点
override fun toString() = "kotlin.Unit"
}
顶层函数
《kotlin核心编程》书中提到,kotlin中类和函数都是一等公民。这个比喻让我一度困惑,直到我反编译了下面的代码
//新建一个demo.kt文件
//这个a就是一个顶层属性
var a = "fuck code"
//这个demoOne就是一个顶层函数
fun demoOne() {
print(a)
}
public final class DemoKt {
@NotNull
private static String a = "fuck code";
@NotNull
public static final String getA() {
return a;
}
public static final void setA(@NotNull String var0) {
Intrinsics.checkParameterIsNotNull(var0, "<set-?>");
a = var0;
}
public static final void demoOne() {
String var0 = a;
//这里会多一个奇怪的bool变量,没研究过kotlin编译
//有懂的大佬可以指教一下
boolean var1 = false;
System.out.print(var0);
}
}
顶层函数就是一个直接声明中某文件中的函数。这个文件会被编译成一个不可被继承的类,类名就是文件名。而这个文件中的顶层函数被编译成该类的静态成员方法。顶层属性也是相同的。
所谓一等公民,就是能不能在一个文件里直接声明。
应用
顶层函数的用法,目前我只接触过一种,代码如下
//创建一个 MyExtensions.kt 文件
//用来收集该package中的所有扩展函数
//这是个扩展函数,也是个顶层函数
fun String.mExtensionOne(): Unit {
print("Extension one")
}
fun Int.mExtentionTwo(a: Int): Int {
return a+1
}
成员函数
这个概念就很简单,函数作为一个类的成员,称为成员函数。
作为初学者,我一度迷惑于对于一个类,函数和属性究竟有什么区别。《深入理解JVM》书中解释到,类被解析成class文件,其中存在字段表和方法表两种数据结构。字段表修饰成员变量,方法表修饰成员函数。二者都有一个属性叫做属性表,这也是个表结构,里面存在着描述这个属性或者方法的信息。而属性表里有一个属性叫做“Code”,这个属性还是个表结构,“Code”表里有一个属性叫做"code",这个属性的value才是函数体被编译后真正的字节码。所以,一个类的函数和属性,都是一个个字段表或方法表,它们有一个成员是属性表结构,里面存放着描述信息。了解到这个程度足以应付开发了。
关于成员函数,还有一点需要注意。下面这段代码中,有三个同名方法demo。只有第一个demo才是A类的成员。在kotlin中
- inner修饰的内部类,持有外部类的一个隐式的引用,注意不是this指针。在内部类里,可以直接调用外部类的属性和方法,但外部类无法直接调用内部类的属性和方法。内部类的this指针仍然是指向内部类的,遇到和外部类属性或方法同名时,也是内部类优先。
- 嵌套类可以粗略理解为外部类的平级类,外部类无法直接调到嵌套类的属性和方法,嵌套类也无法直接调到外部类的属性和方法。(是无法直接,并不是不能,直接new一个对象,那当然是随便调了)
class A {
var one = "one"
fun demo(x: String = ""){
print("A $x\n")
}
fun Aprint() {
demo() //运行结果 A