深入kotlin - 基础语法
Posted 颐和园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入kotlin - 基础语法相关的知识,希望对你有一定的参考价值。
变量常量
变量和 swift 一样,用 var 声明:
var str:String = "hello"
kotlin 同样支持类型推断,可以根据变量初始化值推断变量类型。因此上句可以写成:
var str = "hello"
常量和 swift 不同,用 val 而不是 let 声明:
val a = 10
这种常量值 kotlin 中叫做“不可变量”,表示该常量不可改变(除了声明的时候可以赋值)。
此外还有一种常量,用 const val,二者的区别在于:
- const val 默认是 public final static ,val 默认是 private final static ,二者的访问级别不同;
- const val 值允许在 top-level 和 object 中声明,二者的使用范围不同。
- 访问 val 时是通过 getter 访问器访问,而 const val 不是,二者效率不同。
从 swift 的角度看,可以把 const val 默认是 public static let,而 val 默认是 private let。
注释
和 swift 相同,分为单行注释(//),多行注释(/* */
),文档注释(/** */
)。同时,Kotlin 支持注释嵌套:
/*
/*
*/
*/
函数
fun sum(a: Int, b: Int): Int return a+b
函数定义的关键字使用 fun 返回值类型直接放在参数列表之后,参数定义形式:参数名:类型。
可以对函数直接赋值
fun sum(a: Int, b: Int) = a+b
如果函数体仅仅是一个表达式,则可以用该表达式对函数进行赋值,表达式计算结果即函数的返回值,返回值类型自动推断。
Void vs Unit
Java 中方法无返回值可以使用 void 关键字,在 kotlin 中则使用 Unit 替代(实际上Unit 不是一个 object 类型),也可以直接省略不写。但是与 void 不同,Unit 可以被 return 语句返回:
fun test(name: String): Unit // Unit 可以省略
... ...
return Unit // 此句可以省略
单表达式函数
函数体如果只包含一个表达式,则可以定义为:
fun add(a: Int, b:Int) = a+b
返回类型省略,通过表达式类型推断。
显式返回类型
对于有方法体的函数,必须显示指定返回类型(除非返回 Unit, Unit 可以省略):
fun add(a:Int, b: Int): Int // 返回类型不可省略,Kotlin 不会推断有函数体的函数返回类型
return a+b
参数默认值
如果要为函数参数提供默认值,在参数类型后使用 =
即可。
此外,子类重写的方法会使用与父类相同的默认参数值。如果要覆盖的方法有默认参数,方法签名中不能重写默认值,比如:
open class A
open fun method(a: Int, b: Int = 4) = a + b
class B: A()
override fun method(a: Int, b: Int) = a + b
B 中,method 方法不能再覆盖默认参数 b。如果你显式指定 b:Int = 4 编译会报错。也就是说,子类无法重写父类方法中的默认参数值。
如果默认参数位于其他无默认值的参数前面,比如:
fun test(a: Int = 1, b: Int) = println(a-b)
那么要使用这个默认值,那么其他参数必须显式指定参数名:
test(b = 3)
这是为了避免语义模糊,因为如果使用 test(3),那么根据参数顺序传参,会将 3 赋值给 a 而非 b,导致 b 没有初值。
显式指定参数名(具名参数)
有点奇怪,传参时指定参数名使用 = 号而非 : 号:
test(b = 2) // swift 语法: test(b: 2)
如果在调用方法时,既有位置参数又有具名参数,则所有的位置参数都必须位于具名参数之前,比如这样是可以的:
foo(1, x = 2)
反过来是不允许的:
foo(x = 2, 1)
注意,当在 kotlin 中调用 java 方法时,不能使用具名参数,因为字节码中不一定总说保留方法参数名。
可变参数
可变参数使用关键字 vararg 声明:
fun test(vararg args: String)
println(args.javaClass) // 打印:class [Ljava.lang.String]
args.forEach println(it)
... ...
test("a","b","c")
实际上,可变参数是一个数组类型。但 kotlin 不允许直接向可变参数赋值数组类型,必须进行转换:
test( args = *arrayOf("a","b","c"))
这里,*
叫做分散运算符,可以将数组打散成为可变参数可以接受的类型。但是可以只传递一个元素:
test(args = "a")
方法只能有一个可变参数,如果它不是最后一个参数,那么其他参数必须通过具名参数传递,如果其后的参数是函数类型,那么还可以使用尾随闭包形式。
fun <T> convert2List(vararg elements:T):List<T>
val result = ArrayList<T>()
elements.forEach result.add(it)
return result
... ...
println(conver2List("A","B","C")) // 打印 ABC
var elements = arrayOf("A","B","C")
println(convert2List("1","2","3", *elements))// 打印 123ABC
*elements 中的 * 是分散运算符,可以将数组转换成与可变参数兼容的类型。
中缀运算符
函数可以通过中缀运算符来调用,需要满足 2 个条件:
- 必须是成员函数或者扩展函数
- 只有一个参数
定义时使用 infix 关键字:
class TestClass(private var a: Int)
infix fun add(b: Int) = this.a + b // 1
... ...
val testClass = TestClass(2)
println(testClass.add(5)) // 2
println(testClass add 5) // 3
- add 函数是一个成员函数,通过 infix 关键字实现了一个中缀运算。
- 以标准的方法调用方式进行调用。
- 以中缀运算符的形式进行调用。
内联函数
内联函数在编译时会将函数代码复制到调用处,从而减少函数栈的创建和释放开销。缺点是字节码体积增大。
定义内联函数使用 inline 关键字,除此外和普通函数并无差别。
inline fun myCalculate(a:Int, b: Int) = a+b
lambda 表达式
lambda 表达式相当于匿名函数,格式如下:
- 用 包裹
- 参数列表位于 -> 箭头符号之前,参数类型可以省略(但变量声明中不可省略)
- 执行语句位于 -> 之后
- 如果某个参数无用,可以用占位符 _ 表示
- 最后一个表达式的值作为 lambda 表达式的返回值(也可以使用"全限定语法’'显式地 return - return@方法名 )
- 内置 it 关键字
当函数的最后一个参数是 lambda 表达式,调用时可以采用尾随闭包形式。
val sub = a: Int, b: Int -> a-b // 带参数
val action = println("hello world") // 无参数
val add: (Int, Int) -> Int = a, b -> a+b // 表达式中参数类型可以省略,但 add 变量的声明中,参数类型不能省略
strings.filter it.contains("h").forEach println(it) // 内置 it 关键字
strings.filter
val myFilter = it.length > 3
return@filter myFilter // 全限定语法 return
匿名函数
匿名函数使用 fun 关键字定义,形式如下:
fun(x: Int, y:Int) = x+y
strings.filter(fun(item):Boolean = item.length>3) // 匿名函数作为参数
Package
Kotlin 中的 Package 概念和 Java 稍有不同,它的名字和存储它的目录结构不必相同,它更多是一种“命名空间”的概念,主要用于解决命名冲突。
智能类型推断
在 java 中,短长度的类型可以直接赋值给长长度类型的变量,比如将 int 赋值给 long。在 kotlin 中不允许,需要进行显示类型转换。
var a: Int = 0
var b: Byte = 127
a = b.toInt()
但是,在代码中,如果明确已经可以推断出某个变量的确切类型,那么这个变量就可以直接当成这个类型使用,甚至不需要程序员进行显式类型转换,kotlin 会自动进行这种转换,例如:
fun uppercase(x: Any): String?
if (x is String )
return x.toUpperCase()
return null
注意 x 参数可能是任意类型(类型不确定),所以我们使用了根类 Any。kotlin 的类型判断使用 is 关键字(相对于 Java 的 instanceof)。此外,注意在 if 判断之后,x 已经被 kotlin 自动识别为 String 了,所以可以直接调用 String 的 toUpperCase 方法而不用进行类型转换(如果是 Java 则需要)。这也是 kotlin 的类型推断和自动类型转换为我们提供的便利。
以上是关于深入kotlin - 基础语法的主要内容,如果未能解决你的问题,请参考以下文章