在19年的Google I/O大会上,Kotlin 成为 Android 开发首选语言。而著名的OkHttp 已经开始用 Kotlin 进行重写工作。是时候通过写博客归纳来巩固Kotlin基础知识。
Kotlin中使用val关键字声明常量(即变量初始化之后不可再次赋值),var关键字声明变量。
变量定义时可以不显示指定类型,编译器会根据其初始化器的类型进行推断。
//自动推断出 `Int` 类型 var daqi = 1 //可以显式地指定变量的类型 val a :String = "daqi" //如果变量没有初始化器,需要显式地指定它的类型 val c: Int c = 3 复制代码
当编译器能确保val变量只有 唯一 一条初始化语句会 被执行 ,可以根据条件对它初始化不同的值。
val daqi:String var isChange = true if (isChange){ daqi = "1" }else{ daqi = "2" } 复制代码
val变量的 引用自身是不可变 的,但是它指向的 对象是可变 的。
val languages = arrayListOf("Java") languages.add("Kotlin") 复制代码
var 关键字允许变量改变自己的值,但不能改变自己的类型。
var daqi = 1 //编译不通过 daqi = "" 复制代码
(三)、定义函数 Kotlin 中使用 fun 关键字声明函数。 完整的函数定义:
修饰符(默认public)+ fun + 方法名 + (参数列表) :返回值 { 函数体 } 复制代码
public fun daqi(name:String):String { } 复制代码
当单个表达式构成完整的函数体时,可以直接去掉 {} 和 return 语句,函数的返回值类型编译器也会自动推断。这种函数就是表达式函数。
fun sum(a: Int, b: Int) = a + b 复制代码
语句和表达式的区别在于:
在 Java 中,所有的控制结构都是语句。而在 Kotlin 中,除了循环( for, do 和 do/while )以外大多数控制结构都是表达式(例如:if、 when 以及 try 属于表达式)
表达式函数不光用在些简单的单行函数中 ,也可以用在对更复杂的单个表达式求值的函数中。
fun max(a: Int, b: Int ) = if (a > b) a else b 复制代码
类似Java的返回值类型为void的函数
fun daqi():Unit{ } 复制代码
可省略Unit类型
fun daqi(){ } 复制代码
在 Java 中,当需要打印变量描述和其值时,往往如下打印:
String str = "daqi"; System.out.println("str = " + str); 复制代码
Kotlin支持 字符串模板 ,可以在字符串中引用局部变量,但需要在变量名前加上$。表达式会进行 静态检查 , 如果$引用一个不存在的变量,代码不会编译通过。
val str = "daqi" printlin("str = $str") 复制代码
$不仅限于引用于简单的变量名称,还可以引用更复杂的表达式。
val daqi = intArrayOf(1,2,3) println("${daqi[0]}") 复制代码
在$引用的表达式内(即花括号{}中)可以直接嵌套双引号
println("daqi的第一个元素: ${if(daqi.size > 0) daqi[0] else "null"}") 复制代码
当需要打印$符号时,可以对其进行转义,或者利用表达式对其进行打印:
//打印结果为:$str val str = "daqi" println("/$str") ${'$'}daqi 复制代码
在java中定义一个简单类:
public class Person{ private final String name; public Person(String name){ this.name = name; } public String getName(){ return name; } } 复制代码
用Kotlin写的Person类(默认public修饰)
class Person(val name:String) 复制代码
延伸:想知道Kotlin对应的具体Java实现,可以通过idea的工具,对Kotlin文件进行反编译:
Tools ->Kotlin -> Show Kotlin Bytecode -> 右侧弹出字节码框 ->左上角 Decompile按钮
整体基本和Java类相似,但是该类被定义为final类,即太监类,不可被继承。
在Kotlin中,当你声明属性的时候,也就声明了对应的访问器(即get和set)
val属性只有一个getter,var属性既有 getter 和 setter。 声明一个属性的完整语法:
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>] 复制代码
其中,初始化器,getter 和 setter 都是可选的。如果类型可以从初始化器或者getter 返回值中推断出来,也可以省略。 访问器的默认实现非常简单,即对对应变量的返回和更改。
当需要在值被访问或修改时提供额外的逻辑,可以通过自定义getter和setter
在Kotlin中的实现
class Rectangle(val height:Int,val width:Int){ val isSquare:Boolean get(){ return height == width } } 复制代码
对应的Java实现:
在Kotlin中实现:
class Rectangle(val height:Int,val width:Int){ var isSquare:Boolean = false set(value) { field = value } } 复制代码
在setter方法中,使用特殊的标识符field来访问支持字段(幕后字段)的值。具体幕后字段后面再说。
Kolin中,名称以is开头的变量。转换成对应的Java get 和 set 方法时,getter不会添加任何的前准,setter名称中的is会被替换成set。
class Person( var isDaqi:Boolean = false ) 复制代码
该对象在Java中的访问器为:
Kotlin中 while 和 do-while循环,其语法与Java的基本没区别。
而for循环仅以一种形式存在,和for-each差不多。但用 in 取代了 :
fun iterationArray(args:Array<String>){ for (str in args) { } } 复制代码
for循环还可以遍历区间。区间本质上是两个值之间的间隔。使用..运算符表示区间:
val oneToTen = 1..10 复制代码
Kotlin中的区间是闭合区间,即结束值也是区间的一部分。而区间默认迭代步长为1。
可以通过step关键字修改步长
for(i in 0..100 step 2){ } 复制代码
遍历时,i变量其增长幅度将变为2,即每次迭代时都会自增2。
如果想要一个半闭合区间,可以使用until关键字
val oneToNine = 1 until 10 复制代码
如果需要一个倒序的区间,可以使用downTo关键字
val tenToOne = 10 downTo 1 复制代码
此时,将从10一直递减到1进行循环。downTo表示的区间也是一个闭合区间。
如果想实现普通for循环一样,对数据索引进行循环,可以使用数组的indices变量
for (i in args.indices) { println(args[i]) } 复制代码
在Kotlin中,if是一个表达式,即它会返回一个值。
if的分支为代码块时,最后的表达式将作为该代码块的值:
val max = if (a > b) { print("Choose a") a } else { print("Choose b") b } 复制代码
Kotlin中的when,对应的是Java中的switch,但比起更加强大。
when 将它的参数与所有的分支条件按顺序比较,直到某个分支满足条件,执行其分支对应的代码。当多分支需要用相同的方式处理时,可以把其放在一起,用逗号分隔。
当when作为表达式使用时,必须有 else 分支, 除非编译器检测出所有的可能情况都已经被覆盖。(例如枚举类)
when (x) { 0, 1 -> print("x == 0 or x == 1") 2 -> print("x == 2") else -> print("otherwise") } 复制代码
有没有发现,没有了那繁琐的case和break!!
when的参数可有可无,并且无限制类型!而且可以用任意表达式作为分支条件。(switch要求必须使用常量作为分支条件~)
when作为表达式函数的的函数体,直接使用函数的参数,自身并无设置参数。并在条件语句中使用in或者!in 判断一个变量是否在区间或者集合中。
fun daqi(num:Int,intArray: IntArray) = when{ num in 1..10 -> "1~10" num !in intArray -> "no in Array" else -> "other" } 复制代码
when 也可以用来取代 if-else if链.
fun daqi(num:Int) = when{ num < 0 -> "小于0" num >0 && num < 10 -> "1..10" else -> "other" } 复制代码
is可以检查一个变量是否是某种类型,再配合智能转换(检查过某个变量的类型后,不再需要再转换它,直接可以把它当作检查过的类型使用)可以很方便的对进行类型安全操作。
//Any类似于Java的Object fun daqi(num:Any) = when(num){ is String -> { //此时num已经为String类型 println("该变量类型为String,获取其长度") println(num.length) } is Int -> { //此时num已经为Int类型 println("该变量类型为Int,将其与10进行对比") num.rangeTo(10) } else -> "other" } 复制代码
Kotlin的异常处理与Java类似。当抛出异常时,不需要使用new关键字创建异常实例。
var daqi:String? = null if (daqi == null){ throw NullPointerException("daqi is null") } 复制代码
try也可以作为表达式,将其赋值给变量。当代码执行正常,则try代码块中最后一个表达式作为结果,如果捕获到异常,对应catch代码块中最后一个表达式作为结果。finally 块中的内容不会影响表达式的结果。
fun readNumber(reader:BufferedReader):Int? = try{ Integer.parseInt(reader.readLine()) }catch(e:NumberFormatException){ null }catch (e:NullPointerException){ null }finally { reader.close() } 复制代码
可以有0到多个 catch 块。finally 块可以省略。 但是 catch 与 finally 块至少应该存在一个。
kotlin中用两个关键字enum和class声明枚举类。enum是一个软关键字,只有当它出现在class前面时才有特殊的意义。
声明普通枚举类:
enum class Color{ RED,BLUE } 复制代码
声明带属性的枚举类:
enum class Color(val r:Int,val g:Int,val b:Int){ RED(255,0,0),BLUE(0,0,255); fun rgb = (r * 256 + g) * 256 + b } 复制代码