Kotlin知识归纳(一) —— 基础语法

前序

        在19年的Google I/O大会上,Kotlin 成为 Android 开发首选语言。而著名的OkHttp 已经开始用 Kotlin 进行重写工作。是时候通过写博客归纳来巩固Kotlin基础知识。

(一)、语法上的变更

  • 创建对象不需要new关键字
  • 语句不需要;结尾,加;也无所谓
  • 变量类型后置,即变量名在前,变量类型在后。例如 str:String
  • 使用 println() 替代 System.out.println()

(二)、定义变量

        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 + 方法名 + (参数列表) :返回值 {
        函数体
}
image
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")
println(${'$'}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按钮

image
image

整体基本和Java类相似,但是该类被定义为final类,即太监类,不可被继承。

setter 和 getter

    在Kotlin中,当你声明属性的时候,也就声明了对应的访问器(即get和set)

    val属性只有一个getter,var属性既有 getter 和 setter。
声明一个属性的完整语法:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

    其中,初始化器,getter 和 setter 都是可选的。如果类型可以从初始化器或者getter 返回值中推断出来,也可以省略。
访问器的默认实现非常简单,即对对应变量的返回和更改。

当需要在值被访问或修改时提供额外的逻辑,可以通过自定义getter和setter

自定义getter

在Kotlin中的实现

class Rectangle(val height:Int,val width:Int){
    val isSquare:Boolean
        get(){
            return height == width
        }
}

对应的Java实现:

image

自定义setter

在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中的访问器为:

image

(五)、迭代

Kotlin中 while 和 do-while循环,其语法与Java的基本没区别。

而for循环仅以一种形式存在,和for-each差不多。但用 in 取代了 :

fun iterationArray(args:Array<String>){
    for (str in args) {

    }
}

    for循环还可以遍历区间。区间本质上是两个值之间的间隔。使用..运算符表示区间。

    Kotlin中的区间是闭合区间,即结束值也是区间的一部分。而区间默认迭代步长为1。

val oneToTen = 1..10

    可以通过step关键字修改步长。遍历时,i变量其增长幅度将变为2,即每次迭代时都会自增2。

for(i in 0..100 step 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])
}

(六)、if表达式

在Kotlin中,if是一个表达式,即它会返回一个值。

if的分支为代码块时,最后的表达式将作为该代码块的值:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

(七)、when表达式

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中的异常

    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
}

如果在枚举类型中定义任何方法,需要使用分号;把枚举常量列表和方法定义分开。

参考文献:

android Kotlin系列:

Kotlin知识归纳(一) —— 基础语法

Kotlin知识归纳(二) —— 让函数更好调用

Kotlin知识归纳(三) —— 顶层成员与扩展

Kotlin知识归纳(四) —— 接口和类

Kotlin知识归纳(五) —— Lambda

Kotlin知识归纳(六) —— 类型系统

Kotlin知识归纳(七) —— 集合

Kotlin知识归纳(八) —— 序列

Kotlin知识归纳(九) —— 约定

Kotlin知识归纳(十) —— 委托

Kotlin知识归纳(十一) —— 高阶函数

Kotlin知识归纳(十二) —— 泛型

Kotlin知识归纳(十三) —— 注解

Kotlin知识归纳(十四) —— 反射

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342