Kotlin基本语法
修饰符
| 修饰符 | 说明 |
|---|---|
| private | 只能在当前类中访问 |
| protected | 只能在当前类和子类中访问 |
| public | 任何类都可以访问(默认) |
| internal | 只能在当前模块中访问 |
声明变量
Kotlin使用val声明不可变变量,使用var声明可变变量
1 | val name: String = "小明" |
kotlin类型推导机制:自动推导变量的数据类型,不需要显式指定
1 | val name = "小明" |
但是如果延迟赋值,就必须显式指定类型
1 | // 只声明不赋值 |
kotlin中默认不允许变量为null,如有必要,必须使用?修饰
1 | val name: String? = null |
逻辑控制
if
if语法常规使用和Java基本一致
1 | fun getType(any : Any) : String { |
if-else语句可以作为表达式给变量赋值,每个条件代码块中的最后一行代码作为该条件的返回值
1 | val isRange: Boolean = if (value in 0..100) { |
进一步简化
1 | val isRange: Boolean = if (value in 0..100) true else false |
when
when语句可以替代if、else if、else,并且也可以有返回值
1 | fun getType(any: Any?) : String { |
进一步简化
1 | fun getType(any: Any?) = when (any) { |
for
for多用于遍历
1 | // 单列集合遍历 |
while
while多用于循环
1 | var i = 0 |
函数
Kotlin使用fun声明函数
1 | fun test() { |
如果未注明返回值类型,会自动推导为Unit类型,即无返回值
1 | fun test(): Unit { |
返回值类型放在形参列表后面,使用:分隔
1 | fun getString(): String { |
函数只有一个表达式时,可以省略花括号和return关键字,用一个等号代替
1 | fun getString(): String = "Hello" |
使用=号时,由于类型推导机制的存在,可以省略返回值类型
1 | fun getString() = "Hello" |
函数也可以作为参数传递给其他函数
1 | fun main() { |
类和接口
声明类
声明类使用class
1 | class User() { |
类中没有属性和方法时,可以省略构造器括号和花括号
1 | class User |
Kotlin中创建对象不需要使用new关键字
1 | val user = User() |
构造函数
Kotlin中构造函数分为主构造函数和次构造函数
主构造函数
主构造函数写在类名后面,使用constructor(...)关键字声明,constructor关键字可以省略,括号中是主构造函数的形参列表
1 | class User constructor() |
如果类没有显式声明任何主、次构造函数构造函数,则会自动生成无参的主构造函数,如下三种写法实际上完全一致
1 | class User |
主构造函数没有函数体,如果要在主构造函数中写逻辑,需要写在init结构体中
1 | class User(var name: String, var age: Int) { |
主构造函数中使用var或val声明的形参,和常规成员变量的调用完全一致
1 | class User(var name: String, var age: Int) |
但如果主构造函数中的形参没有var或val声明,则只能在init结构体和类中调用
1 | class User(name: String, age: Int){ |
次构造函数
声明次构造函数使用constructor关键字,允许有多个次构造函数,而无主构造函数
1 | // 注意此时的类名后没有括号,说明没有显式声明主构造函数 |
当一个类同时有主、次构造函数时,所有次构造函数必须直接或间接的调用主构造函数
1 | class User() { |
继承
Kotlin中的类默认不可继承,类和其中的方法都是final的,在类名前添加open关键字使类可以被继承
1 | open class User |
类的继承使用:符号,Kotlin中所有类默认继承Any类
1 | class IKun() : Any() |
父类User之所以带有(),是因为在类继承时子类的构造函数会调用父类的构造函数,如果父类没有显式定义构造函数,子类继承时调用父类的无参构造函数
1 | class IKun : User() |
如果父类有多个构造函数,子类继承时只需要选择调用一个构造函数即可,通过()中的参数来区分调用哪个构造函数
1 | // 父类的主构造函数无形参 |
如果子类中只有次构造函数,没有主构造函数,那么继承父类时不能使用(),而是在次构造函数中使用super关键字调用父类的构造函数
1 | class IKun : User { |
数据类
Kotlin中提供了数据类,使用data关键字声明,会自动生成equals()、hashCode()、toString()方法
1 | data class User(private val name: String, private val age: Int) |
单例类
Kotlin中提供了单例类,使用object关键字声明,会自动生成一个实例,并且是线程安全的
1 | object Singleton { |
在其他kotlin类中调用
1 | Singleton.singletonTest() |
经过反编译发现,等同于
1 | class Singleton { |
在java类中的调用object关键字声明的类,用如下方式
1 | Singleton.INSTANCE.singletonTest() |
接口
声明接口使用interface关键字,接口没有构造函数
1 | interface IKun { |
实现接口使用:符号,实现多个接口使用,分隔,重写方法或成员变量时,需要使用override关键字
1 | class XiaoHeiZi : IKun { |
调用接口中的方法
1 | val iKun: IKun = XiaoHeiZi() |
伴生对象
Kotlin中没有static关键字,伴生对象类似于Java中的静态,使用companion object声明伴生对象
1 | class User { |
集合
List
创建只读List使用listOf()函数。在kotlin中,List是只读的
创建可变MutableList, 使用mutableListOf()函数。在kotlin中,MutableList是List的子接口,可以进行增删改操作
1 | val mutableList: MutableList<String> = mutableListOf("a", "b") |
获取list中的第一个或最后一个元素,分别使用.first()和.last()函数。不过.first()和.last()不是List特有的函数,而是kotlin.collections包中定义的扩展函数,适用于所有实现Collection的类
1 | val firstItem: String = list.first() |
获取list中元素的数量,使用.count()函数
1 | val itemCount: Int = list.count() |
检测元素是否在list中,使用in关键字,其背后实际上是通过contains(E)实现的
1 | val containsA: Boolean = "a" in list |
add和remove就不说了
Set
同List,创建只读Set使用setOf()函数。在kotlin中,Set是只读的
创建可变MutableSet, 使用mutableSetOf()函数。在kotlin中,MutableSet是Set的子接口,可以进行增删改操作
1 | val mutableSet: MutableSet<String> = mutableSetOf("a", "b") |
Map
创建只读Map使用mapOf()函数。在kotlin中,Map是只读的
创建可变MutableMap, 使用mutableMapOf()函数。在kotlin中,MutableMap是Map的子接口,可以进行增删改操作
1 | val mutableMap: MutableMap<String, String> = mutableMapOf("a" to "1", "b" to "2") |
向可变MutableMap中添加或修改键值对,可以使用如下类似给数组赋值的语法,其背后实际上是通过put(K, V)方法实现的
1 | mutableMap["c"] = "3" // 添加键值对 |
从可变map中移除元素,使用.remove()函数
1 | mutableMap.remove("b") |
获取map中元素的数量,使用.count()函数
1 | val itemCount: Int = map.count() |
检测map中是否已包含特定键,使用.containsKey()函数
1 | val containsKeyA: Boolean = map.containsKey("a") |
检测键或值是否在map中,使用in操作符
1 | val containsKeyA: Boolean = "a" in map.keys |
获取map的键或值的集合,分别使用keys和values属性
1 | val keys: Set<String> = map.keys |
Lambda表达式
Lambda表达式的结构如下
1 | { 参数名1: 参数类型, 参数名2: 参数类型 -> 函数体 } |
函数式接口
函数式接口是指只包含一个抽象方法的接口,在Kotlin中,函数式接口使用fun interface关键字声明
Java中的函数式接口使用@FunctionalInterface注解声明
如果一个函数所需的参数是函数式接口,那么可以直接将Lambda表达式作为参数传递给函数
1 | /** |
创建TransmitExecutor对象时,需要传入OnContentTransmitter的匿名类,如下
1 | TransmitExecutor (object : OnContentTransmitter { |
使用Lambda表达式可以简化代码
1 | val lambda = { content: String -> |
Lambda表达式不必声明为变量,直接写入
1 | TransmitExecutor({ content: String -> |
Lambda表达式的参数类型可以省略,编译器会自动推导
1 | TransmitExecutor({ content -> |
如果Lambda表达式是最后一个参数,那么可以将其放在括号外面;如果Lambda表达式是唯一一个参数,那么可以省略括号
1 | TransmitExecutor { content -> |
此外如果Lambda表达式只有一个参数,参数可以使用it关键字代替
1 | TransmitExecutor { |
同样如果一个函数需要的参数是函数式接口时,和上面的步骤一致
1 | /** |
函数作为参数
Lambda表达式可以作为参数传递给函数,如下,在子线程中执行传入的函数
1 | fun execute(func: (content: String) -> Boolean) { |
正常调用时
1 | execute (fun (content: String): Boolean { |
通过Lambda表达式简化代码
1 | execute { |
函数作为返回值
Lambda表达式也可以作为函数的返回值,如下函数的返回值也是函数,可以简化
1 | /** |
调用时
1 | getFunc().invoke("cxk", "唱、跳、rap、篮球") |