Getting Started
基本语法
定义包
包定义应位于源文件顶部
package my.demo
import java.util.*
// ....
包名和目录不需要匹配:源文件可以放在文件系统的人员位置
定义函数
- 返回类型为
Int
类型且有两个Int
参数的函数
fun sum(a: Int, b: Int): Int{
return a + b
}
- 定义一个只有表达式函数体以及一个自推导型的返回值
fun sum(a: Int, b:Int) = a + b
- 返回一个没有意义的值
fun printSum(a: Int, b: Int): Unit{
println("sum of $a and $b is ${a + b}")
}
Unit可以省略
fun printSum(a: Int, b: Int){
println("sum of $a and $b is ${a + b}")
}
定义局部变量
常量
val a: Int = 1 //立即初始化
val b = 2 //推导出Int类型
val c: Int //没有初始化时必须声明类型
c = 3 //赋值
变量
var x = 5 //推导出Int类型
x += 1
更多查阅属性和字段
注释
与Java和JavaScript一样,Kotlin支持单行注释和块注释
// 单行注释
/* 块注释
*/
与Java不一样的是,Kotlin中的块注释可以级联 参看Documenting Kotlin Code学习更多关于文档化注释的语法。
使用字符串模板
var a = 1
val s1 = "a is $a"
a = 2
val s2 = "${s1.replace("is","was")},but now is $a"
使用条件表达式
fun maxOf(a: Int, b: Int){
if(a > b){
return a
} else {
return b
}
}
把if作为表达式
fun maxOf(a: Int, b: Int) = if (a > b) a else b
使用可空变量和空值检查
当空值可能出现时,应明确指出该引用为可空 如果str
不包含整数时,返回null
fun parseInt(str:String):Int?{
}
使用一个返回可空值的函数
fun parseInt(str:String):Int?{
return str.toIntOrNull()
}
fun printProduct(arg1:String,arg2:String){
val x = parseInt(arg1)
val y = parseInt(arg2)
if(x != null && y != null){
println(x * y)
}else{
println("either '$arg1' or '$arg2' is not a number")
}
}
fun main(args:Array<String>){
printProduct("6","7")
printProduct("a","7")
printProduct("a","b")
}
or
//...
if(x == null){
println("Wrong number format in arg1: '$arg1'")
return
}
if(y == null){
println("Wrong number format in arg2: '$arg2'")
return
}
println(x * y)
使用类型检查和自动转换
使用is运算符检查表达式是否是某个类型的实例,如果对不可变的局部变量或属性进行了类型检查,就没有必要显示转换。
fun getStringLength(obj:Any):Int?{
if(obj is String){
return obj.length //obj自动转换为String
}
return null
}
fun main(args:Array<String>){
fun printLength(obj:Any){
println("'$obj' string length is ${getStringLength(obj) ?:"...err,not a string"}")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or
fun getStringLength(obj:Any):Int?{
if(obj !is String) return null
return obj.length //obj自动转换为String
}
or
fun getStringLength(obj:Any):Int?{
if(obj is String && obj.length > 0){ //obj自动转换为String
return obj.length
}
return null
}
使用for循环
fun main(args:Array<String>){
val items = listOf("apple","banana","kiwi")
for(item in items){
println(item)
}
}
or
fun main(args:Array<String>){
val items = listOf("apple","banana","kiwi")
for (index in items.indices){
println("item at $index is ${items[index]}")
}
}
使用while循环
fun main(args:Array<String>){
val items = listOf("apple","banana","kiwi")
var index = 0
while (index<items.size){
println("item at $index is ${items[index]}")
index++
}
}
使用when表达式
fun describe(obj:Any):String =
when (obj){
1 -> "One"
"Hello" ->"Greeting"
is Long ->"Long"
!is String -> "Not a string"
else -> "Unkown"
}
fun main (args:Array<String>){
println(describe(1))
println(describe("Hello"))
println(describe(1000L))
println(describe(2))
println(describe("other"))
}
Range使用
使用in
操作符检查数值是否在某个范围内
fun main(args:Array<String>){
val x = 10
val y = 9
if(x in 1..y+1){
println("fits in range")
}
}
检查数值是否超出范围
fun main(args:Array<String>){
val list = listOf("a","b","c")
if(-1 !in 0..list.lastIndex){
println("-1 is out of range")
}
if(list.size !in list.indices){
println("list size is out of valid list indices range too")
}
}
在范围内迭代
fun main (args:Array<String>){
for(x in 1..5){
println(x)
}
}
或者使用一个步长
fun main (args:Array<String>){
for(x in 1..10 step 2){
println(x)
}
for(x in 9 downTo 0 step 3){
println(x)
}
}
使用集合
对集合进行迭代
fun main (args:Array<String>){
val items = listOf("apple","banana","kiwi")
for(item in items){
println(item)
}
}
使用in操作符判断集合中是否包含一个对象
fun main (args:Array<String>){
val items = setOf("apple","banana","kiwi")
when{
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
}
使用lambda表达式过滤和映射集合
fun main(args:Array<String>){
val fruits = listOf("banana","avocado","apple","kiwi")
fruits
.filter {it.startsWith("a")}
.sortedBy {it}
.map {it.toUpperCase()}
.forEach {println(it)}
}
惯用语法
下面是Kotlin中经常使用的语法
创建DTOs(POJOs/POCOs)
data class Customer(val name:String,val email:String)
给Customer类提供以下方法
- 给所有属性添加getters,如果为var类型,同时添加setters
- equals()
- hashCode()
- toString()
- copy()
- component1(),component2()
函数默认值
fun foo(a:Int=0,b:String = ""){...}
过滤list
val positives = list.filter{x -> x > 0}
或者
val positives = list.filter { it > 0}
字符串插值
println("Name $name")
实例检查
when(x){
is Foo -> ...
is Bar -> ...
else -> ...
}
遍历map/list列表
for((k,v) in map){
println("$k -> $v")
}
使用ranges
for (i in 1..100) {...} //闭区间,包含100
for (i in 1 util 100) {...} //半开区间,不包含100
for (x in 2..10 step 2) {...}
for (x in 10 downTo 1) {}
if(x in 1..10) {}
只读列表
val list = listOf("a","b","c")
只读map
val map = mapOf("a" to 1,"b" to 2,"c" to 3)
访问map
println(map["key"])
map["key"]=value
懒属性(延迟加载)
val p:String by lazy{
//计算字符串
}
扩展函数
fun String.spaceToCamelCase(){}
"Convert this to camelcase".spaceToCamelCase()
创建单例
object Resource {
val name = "Name"
}
如果非空,则..的简写
val files = File("Test").listFiles()
println(files?.size)
如果非空...否则...的简写
val files = File("Test").listFiles()
println(files?.size ?:"empty")
如果为空执行操作
val data = ...
val email = data["email"]?: throw IllegalStateException("Email is missing!")
如果非空执行操作
val data = ...
data?.let{
//如果不为空执行此代码块
}
返回when声明
fun transform(color:String):Int{
return when(color){
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("invalid color param value")
}
}
try/catch表达式
fun test(){
var result = try{
count()
} catch (e:ArithmeticException){
throw java.lang.IllegalArgumentException(e)
}
//处理result
}
if表达式
fun foo(param:Int){
val result = if (param == 1){
"one"
}else if(param ==2){
"two"
}else {
"three"
}
}
方法使用builder模式返回Unit
fun arrayOfMinusOnes(size:Int):IntArray{
return IntArray(size).apply{ fill(-1) }
}
单个表达式的函数
fun theAnswer() = 42
等价于
fun theAnswer():Int{
return 42
}
这可以与其他语法组合成更加高效、简洁的代码,如when表达式
fun transform(color:String):Int = when(color){
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw java.lang.IllegalArgumentException("invalid color param value")
}
利用with调用一个对象的多个方法
class Turtle{
fun penDown()
fun penUp()
fun turn(degrees:Double)
fun forward(pixels:Double)
}
val myTurtle = Turtle()
with(myTurtle){
penDown()
for(i in 1..4){
forward(100.0)
turn(90.0)
}
penUp()
}
Java7中的try with resources
val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use{ reader ->
println(reader.readText())
}
需要泛型信息的泛型函数的简单方式
//public final class Gson{
// ...
// public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaException{
inline fun <reified T:Any> Gson.fromJson(json):T = this.fromJson(json,T::class.java)
使用一个可空的布尔值
val b:Boolean? = ...
if (b = true){
...
}else{
//'b' is false or null
}
编码公约
本页包含Kotlin语言当前的编码风格
命名风格
如果有疑惑,请默认使用Java编码公约,例如
- 使用驼峰命名法(避免在名称中使用下划线)
- 类名首字母大写
- 方法名和属性名首字母小写
- 使用4空格缩进
- public方法需要写文档,这样就可以出现在Kotlin的Doc中
冒号
当冒号分隔父类和子类时,冒号前有空格,当冒号分隔实例和类型时,冒号前面没有空格
interface Foo<out T : Any> : Bar{
fun foo(a: Int): T
}
Lambdas
在lambdas表达式中,大括号周围要有空格,分隔参数和函数体的箭头周围要有空格。只要有可能,lambda应该放在括号外面传入
list.filter { it > 10 }.map { element -> element * 2 }
在简短而非嵌套的lambda中,推荐使用it
而不是显示的声明参数。在嵌套的lambda中使用参数,参数应该总是显示声明
类头部格式
只有几个参数的类可以写成一行
class Person(id: Int, name: String)
具有较多参数的类应该格式化成每个构造函数参数都在单独的缩进行中。此外,结束括号应该在新行上。如果我们使用继承,那么超类的构造函数调用或实现的接口列表应该与括号位于同一行
class Person(
id: Int,
name: String,
surname: String
) : Human( id, name){
//...
}
对于多个接口,应首先定义超类构造函数的调用,然后每个接口应位于不同的行中
class Person(
id: Int,
name: String,
surname: String
) : Human( id, name),
KotlinMaker {
//...
}
构造函数参数可以使用常规缩进或连续缩进(双倍的常规缩进)
Unit
如果函数返回Unit,则返回类型应该省略
fun foo(){ //": Unit" 此处被省略
}
函数 VS 属性
在某些情况下,没有参数的函数可以与只读属性互换。尽管语义是相似的,但是有一些风格上的约定在什么时候更偏向于另一个。 在下面的情况下,更偏向于属性而不是一个函数。
- 不需要抛出异常
- 拥有O(1)复杂度
- 低消耗的计算(或首次运行结果会被缓存)
- 返回与调用相同的结果