3.函数的定义和调用
Kotlin没有采用它自己的集合类,而是采用的标准的Java集合类。
命名参数。不幸的是,当你调用Java的函数时,不能采用命名参数,不管是JDK中的函数,或者是Android矿建中的函数,都不行。把参数名存到.class
文件是Java 8及其更高版本的一个可选功能,而Kotlin需要保持和Java 6的兼容性。所以,编译器不能识别出调用函数的参数名称,然后把这些参数名对应到函数的定义的地方。
Java的另一个普遍存在的问题是,一些类的重载函数实在太多了。在Kotlin中,可以在声明函数的时候,指定参数的默认值,这样就可以避免创建重载的函数。当使用常规的调用语法时,必须按照函数声明中定义的参数顺序来给定参数,可以省略的只有排在末尾的参数。如果使用命名参数,可以省略中间的一些参数,也可以以你想要的任意顺序只给定你需要的参数。
考虑到Java没有参数默认值的概念,当你从Java中调用Kotlin函数的时候,必须显式地指定所有参数值。如果需要从Java代码中频繁的调用,而且希望它能对Java的调用者更简便,可以用@JvmOverloads
注解它。这个指示编译器生成Java重载函数,从最后一个开始省略每个参数,每个重载函数的默认参数值都会被省略。
消除静态工具类:顶层函数和属性
要改变包含Kotlin顶层函数的生成的类的名称,需要为这个文件添加@JvmName
的注解,将其放到这个文件的开头,位于包名的前面:@file:JvmName("StringUtils")
和函数一样,属性也可以放到文件的顶层。也可以在代码中用顶层属性来定义常量:val UNIX_LINE_SEPERATOR = "\n"
。默认情况下,顶层属性和其他任意属性一样,是通过访问器暴露给Java使用的(如果是val就只有一个getter,如果是var就对应一对getter和setter)。为了方便使用,如果你想要把一个常量以public static final
的属性暴露给Java,可以用const
来修饰它(这个适用于所有的基本数据类型的属性以及String类型)const val UNIX_LINE_SEPERATOR = "\n"
给别人的类添加方法:扩展函数和属性
理论上来说,扩展函数非常简单,它就是一个类的成员函数,不过定义在类的外面。在扩展函数中,可以像其他成员函数一样用this,也可以像其他成员函数一样省略它。
在扩展函数中,可以直接访问被扩展的类的其他方法和属性,就好像是在这个类自己的方法中访问它们一样。注意,扩展函数并不允许你打破它的封装性。和在类内部定义的方法不同的是,扩展函数不能访问私有的或者是受保护的成员。
对于你定义的一个扩展函数,它不会自动地在整个项目范围内生效。相反,如果你要使用它,需要进行导入,就像其他任何的类或者函数一样。这是为了避免偶然性的命名冲突。Kotlin允许用和导入类一样的语法来导入单个的函数。也可以用*
来导入。可以使用关键字as
来修改导入的类或者函数名称。import strings.lastChar as last
实质上,扩展函数是静态函数,它把调用对象作为了它的第一个参数。调用扩展函数,不会创建适配的对象或者任何运行时的额外消耗。这使得从Java中调用Kotlin的扩展函数变得非常简单:调用这个静态函数,然后把接收者对象作为第一个参数传进去即可。和其他顶层函数一样,包含这个函数的Java类的名称,是由这个函数声明的文件名称决定的。
扩展函数的静态性质也决定了扩展函数不能被子类重写。
扩展函数并不是类的一部分,它是声明在类之外的,尽管可以给基类和子类都分别定义一个同名的扩展函数,当这个函数被调用时,它会用到哪一个呢?这里,它是由该变量的静态类型所决定的,而不是这个变量的运行时类型。
如果一个类的成员函数和扩展函数有相同的签名,成员函数往往会被优先使用。
Kotlin的可变参数与Java类似,但语法略有不同:Kotlin在该类型之后不会再使用三个点,而是在参数上使用vararg
修饰符。Kotlin和Java之间另一个区别是,当需要传递的参数已经包装在数组中时,调用该函数的语法。在Java中,可以按原样传递数组,而Kotlin则要求你显式地解包数组,以便每个数组元素在函数中能作为单独的参数来调用。从技术的角度来看,这个功能被称为展开运算符,而使用的时候,不过是在对应的参数前面放一个*
:
fun main(args: Array<String>) {
val list = listOf("args:", *args)
println(list)
}
在中缀调用中,没有添加额外的分割符,函数名称是直接放到目标对象名称和参数之间的。1.to("one")
和1 to "one"
是等价的。
中缀调用可以与只有一个参数的函数一起使用,无论是普通的函数还是扩展函数。要允许使用中缀符号调用函数,需要使用infix
修饰符来标记它。
Kotlin使用与Java中完全相同的正则表达式语法。
fun parsePathUseRegex(path: String) {
val regex = """(.+)/(.+)\.(.+)""".toRegex()
val matchResult = regex.matchEntire(path)
if (matchResult != null) {
val (directory, filename, extension) = matchResult.destructured
println("dir: $directory, filename: $filename, extension: $extension")
}
}
正则表达式写在一个三重引号的字符串中。在这样的字符串中,不需要对任何字符进行转义,包括反斜线。
三重引号字符串的目的,不仅在于避免转义字符,而且使它可以包含任何字符,包括换行符。还可以在多行字符串中使用字符串模板,因为多行字符串不支持转义序列,如果需要在字符串的内容中使用美元符号的字面量,则不需使用嵌入式表达式,val price = """${'$'} $waterprice"""
局部函数可以访问所在函数中的所有参赛和变量。