android kotlin 基础学习 没学习一门新的语言都是从 Helloword 开始
一、Hello World

按照国际惯例,学习一门新的语言通常都是从“Hello World”开始的,在这里也不例外,先看下 Java 中的 main 方法如何用 Kotlin 来表示

fun main(args: Array<String>) {
    println("Hello World")
}

从这里可以看出 Kotlin 相比 Java 有几点不同

用关键字 fun 来声明一个函数
参数类型写在变量名之后
函数可以定义在文件的最外层,不需要把它放在类中
数组就是类。和 Java 不同,Kotlin 没有声明数组类型的特殊语法
使用 println 代替了 System.out.println ,这是 Kotlin 标准库提供的对 Java 标准库函数的封装
可以省略代码结尾的分号

此外,Kotlin 的最新版本已经可以省略 main 方法的参数了
二、变量

在 Java 中,大部分的变量是可变的,意味着任何可以访问到这个变量的代码都可以去修改它。而在 Kotlin 中,变量可以分为 可变变量(var) 和 不可变变量(val) 两类

声明变量的关键字有两个:

val(value)——不可变引用。使用 val 声明的变量不能在初始化之后再次赋值,对应的是 Java 中的 final 变量
var(variable)——可变引用。var 变量的值可以被改变,对应的是 Java 中的非 final 变量

不可变变量在赋值之后就不能再去改变它的状态了,因此不可变变量可以说是线程安全的,因为它们无法改变,所有线程访问到的对象都是同一个,因此也不需要去做访问控制,开发者应当尽可能地使用不可变变量,这样可以让代码更加接近函数式编程风格

此外,在 Kotlin 中一切都是对象,没有像 Java 中那样的原始基本类型,但 byte、char、integer、float 或者 boolean 等类型仍然有保留,但是全部都作为对象存在

看以下例子

fun main(args: Array<String>) {
    //只读变量即赋值后不可以改变值的变量,用 val 声明
    //声明一个整数类型的不可变变量
    val intValue: Int = 100

    //声明一个双精度类型的可变变量
    var doubleValue: Double = 100.0
}

在声明变量时我们通常不需要指明变量的类型,这可以由编译器根据上下文自动推导出来

fun main(args: Array<String>) {
    //在声明变量时我们通常不需要指明变量的类型,这可以由编译器根据上下文自动推导出来
    val intValue = 100
    var doubleValue = 100.0

    //如果只读变量在声明时没有初始值,则必须指明变量类型
    val intValue2: Int
    intValue2 = 10
}

三、数据类型
3.1、基本数据类型

与 Java 不同,Kotlin 并不区分基本数据类型和它的包装类,在 Kotlin 中一切都是对象,可以在任何变量上调用其成员函数和属性

对于基本类型,Kotlin 相比 Java 有几点特殊的地方

数字、字符和布尔值可以在运行时表示为原生类型值,但对开发者来说,它们看起来就像普通的类
Kotlin 对于数字没有隐式拓宽转换,而在 Java 中 int 可以隐式转换为 long
在 Kotlin 中字符不能视为数字
Kotlin 不支持八进制

//在 Kotlin 中,int、long、float 等类型仍然存在,但是是作为对象存在的
val intIndex: Int = 100
    //等价于,编译器会自动进行类型推导
    val intIndex = 100

    //数字类型不会自动转型,必须要进行明确的类型转换
    val doubleIndex: Double = intIndex.toDouble()
    //以下代码会提示错误,需要进行明确的类型转换
    //val doubleIndex: Double = intIndex

    val intValue: Int = 1
    val longValue: Long = 1
    //以下代码会提示错误,因为两者的数据类型不一致,需要转换为同一类型后才能进行比较
    //println(intValue == longValue)

    //Char 不能直接作为数字来处理,需要主动转换
    val ch: Char = 'c'
    val charValue: Int = ch.toInt()
    //以下代码会提示错误
    //val charValue: Int = ch

    //二进制
    val value1 = 0b00101
    //十六进制
    val value2 = 0x123

此外,Kotlin 的可空类型不能用 Java 的基本数据类型表示,因为 null 只能被存储在 Java 的引用类型的变量中,这意味着只要使用了基本数据类型的可空版本,它就会被编译成对应的包装类型

//基本数据类型
 val intValue_1: Int = 200
 //包装类
 val intValue_2: Int? = intValue_1
 val intValue_3: Int? = intValue_1
 //== 比较的是数值相等性,因此结果是 true
 println(intValue_2 == intValue_3)
 //=== 比较的是引用是否相等,因此结果是 false
 println(intValue_2 === intValue_3)如果 intValue_1 的值是100,就会发现 intValue_2 === intValue_3 的比较结果是 true,这就涉及到 Java 对包装类对象的重复使用问题了
如果 intValue_1 的值是100,就会发现 intValue_2 === intValue_3 的比较结果是 true,这就涉及到 Java 对包装类对象的重复使用问题了

3.2、Any 和 Any?

Any 类型是 Kotlin 所有非空类型的超类型,包括像 Int 这样的基本数据类型

如果把基本数据类型的值赋给 Any 类型的变量,则会自动装箱

val any: Any = 100
println(any.javaClass) //class java.lang.Integer

如果想要使变量可以存储包括 null 在内的所有可能的值,则需要使用 Any?

val any: Any? = null

3.3、Unit

Kotlin 中的 Unit 类型类似于 Java 中的 void,可以用于函数没有返回值时的情况

fun check(): Unit {

}

//如果返回值为 Unit,则可以省略该声明
fun check() {

}

Unit 是一个完备的类型,可以作为类型参数,但 void 不行

interface Test<T> {
    fun test(): T
}

class NoResultClass : Test<Unit> {
    
    //返回 Unit,但可以省略类型说明,函数也不需要显式地 return 
    override fun test() {

    }

}

3.4、Nothing

Nothing 类型没有任何值,只有被当做函数返回值使用,或者被当做泛型函数返回值的类型参数使用时才会有意义,可以用 Nothing 来表示一个函数不会被正常终止,从而帮助编译器对代码进行诊断

编译器知道返回值为 Nothing 类型的函数从不正常终止,所以编译器会把 name1 的类型推断为非空,因为 name1 在为 null 时的分支处理会始终抛出异常

data class User(val name: String?)

fun fail(message: String): Nothing {
    throw IllegalStateException(message)
}

fun main(args: Array<String>) {
    val user = User("leavesC")
    val name = user.name ?: fail("no name")
    println(name) //leavesC

    val user1 = User(null)
    val name1 = user1.name ?: fail("no name")
    println(name1.length) //IllegalStateException
}

四、函数

Kotlin 中的函数以关键字 fun 作为开头,函数名称紧随其后,再之后是用括号包裹起来的参数列表,如果函数有返回值,则再加上返回值类型,用一个冒号与参数列表隔开

//fun 用于表示声明一个函数,getNameLastChar 是函数名
    //空括号表示该函数无传入参数,Char 表示函数的返回值类型是字符
    fun getNameLastChar(): Char {
        return name.get(name.length - 1)
    }

    //带有两个不同类型的参数,一个是 String 类型,一个是 Int 类型
    //返回值为 Int 类型
    fun test1(str: String, int: Int): Int {
        return str.length + int
    }

此外,表达式函数体的返回值类型可以省略,返回值类型可以自动推断。对于有返回值的代码块函数,必须显式地写出返回类型和 return 语句

//getNameLastChar 函数的返回值类型以及 return 关键字是可以省略的
    //返回值类型可以由编译器根据上下文进行推导
    //因此,函数可以简写为以下形式
    fun getNameLastChar() = name.get(name.length - 1)

如果无返回值,则可以声明 Unit ,也可以省略 Unit

以下三种写法都是等价的

fun test(str: String, int: Int): Unit {
 println(str.length + int)
 }fun test(str: String, int: Int) {
    println(str.length + int)
}

fun test(str: String, int: Int) = println(str.length + int)