修饰符
修饰符 | 说明 |
---|
private | 只能在当前类中访问 |
protected | 只能在当前类和子类中访问 |
public | 任何类都可以访问(默认) |
internal | 只能在当前模块中访问 |
声明变量
Kotlin
使用val
声明不可变变量,使用var
声明可变变量
1 2
| val name: String = "小明" var age: Int = 18
|
kotlin
类型推导机制:自动推导变量的数据类型,不需要显式指定
1 2
| val name = "小明" var age = 18
|
但是如果延迟赋值,就必须显式指定类型
1 2 3 4 5
| val name: String val age: Int
name = "小明" age = 18
|
kotlin
中不允许变量为null
,如有必要,必须使用?
修饰
1 2
| val name: String? = null var age: Int? = null
|
逻辑控制
条件语句
if
Kotlin
的if
语法和Java
基本一致
1 2 3 4 5 6 7 8 9 10 11 12 13
| fun getType(any : Any?) : String { if (any is String) { return "String" } else if (any is Number) { return "Number" } else if (any is Char) { return "Char" } else if (any == null) { return "null" } return "Unknown" }
|
Kotlin
的if-else
语句是一个表达式,可以返回值,返回值即每个条件代码块中的最后一行代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| fun getType(any : Any?) : String { return if (any is String) { "String" } else if (any is Number) { "Number" } else if (any is Char) { "Char" } else if (any == null) { "null" } else { "Unknown" } }
|
进一步简化
1 2 3 4 5
| fun getType(any: Any?) = if (any is String) "String" else if (any is Number) "Number" else if (any is Char) "Char" else if (any == null) "null" else "Unknown"
|
when
when
语句可以替代if
、else if
、else
,并且也可以有返回值
1 2 3 4 5 6 7 8 9
| fun getType(any: Any?) : String { return when (any) { is String -> {"String"} is Number -> {"Number"} is Char -> {"Char"} null -> {"null"} else -> {"Unknown"} } }
|
进一步简化
1 2 3 4 5 6 7
| fun getType(any: Any?) = when (any) { is String -> "String" is Number -> "Number" is Char -> "Char" null -> "null" else -> "Unknown" }
|
when
语句还可以用于范围判断
1 2 3 4 5 6 7 8
| fun getSope(num : Number) { when (num) { in 0..10 -> println("0..10") in 11..20 -> println("11..20") in 21..30 -> println("21..30") else -> println("out of range") } }
|
1 2 3 4
| val r1: IntRange = 1..10
val r2: IntRange = 1 until 10
|
for
for
语句主要用于遍历
1 2 3 4 5 6 7 8 9 10 11
| val list = listOf(1, 2, 3, 4, 5) for (i in list) { println(i) }
val map = mapOf("a" to 1, "b" to 2, "c" to 3) for ((key, value) in map) { println("$key -> $value") }
|
while
while
语句可以用于循环
1 2 3 4 5
| var i = 0 while (i < 5) { println(i) i++ }
|
函数
Kotlin
使用fun
声明函数
1 2 3
| fun test() { println("test") }
|
如果未注明返回值类型,会自动推导为Unit
类型,即无返回值
1 2 3
| fun test(): Unit { println("test") }
|
返回值类型放在形参列表后面,使用:
分隔
1 2 3
| fun add(i1: Int, i2: Int): Int { return i1 + i2 }
|
函数只有一个表达式时,可以省略花括号和return
关键字,用一个等号代替
1
| fun add(i1: Int, i2: Int): Int = i1 + i2
|
使用=
号时,由于类型推导机制的存在,可以省略返回值类型
1
| fun add(i1: Int, i2: Int) = i1 + i2
|
Kotlin
中函数可以接收另一个函数作为参数
1 2 3 4 5 6 7 8 9 10 11
| fun execute(func : (num1: Int, num2: Int) -> Int) { val result = func(1, 2) println("result = $result") }
val func = fun (num1: Int, num2: Int): Int { return num1 + num2 } execute(func)
|
类和接口
声明类
Kotlin
使用class
声明类
1 2 3 4 5 6 7 8 9 10 11 12 13
| class User (name: String, age: Int){ private var name: String private var age: Int
init { this.name = name this.age = age }
override fun toString(): String { return "User(name='$name', age=$age)" } }
|
类中没有属性和方法时,可以省略花括号
Kotlin
中创建对象不需要使用new
关键字
1 2
| val user = User("小明", 18) println(user.toString())
|
构造函数
Kotlin
中构造函数分为主构造函数和次构造函数,主构造函数只能有一个,次构造函数可以有多个
主构造函数
主构造函数写在类名后面,使用constructor(...)
关键字声明,constructor
关键字也可以省略,括号中是主构造函数的形参列表
1
| class User constructor()
|
如果类没有显式声明任何主构造函数和次构造函数,会自动生成一个无参的主构造函数,如下三种写法实际上完全一致
1 2 3 4 5
| class User
class User()
class User constructor()
|
主构造函数没有函数体,如果要在主构造函数中写逻辑,可以写在init
结构体中
1 2 3 4 5 6 7 8 9
| class User(private var name: String, private var age: Int) { init { printInfo() } private fun printInfo() { println("name: $name, age: $age") } }
|
主构造函数中如果存在var
或val
声明的形参,形参也作为类的成员变量,和常规成员变量的使用完全一致
1 2 3 4
| class User constructor(var name: String, var age: Int)
class User(var name: String, var age: Int)
|
但如果主构造函数中的形参没有var
或val
声明,则不会作为类的成员变量,只能在init
结构体和类中作为被使用,其他函数中无法使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class User(name: String, age: Int){ private val name: String = name private val age: Int = age
init { println("name = $name, age = $age") } override fun toString(): String { return "User(name='${this.name}', age=${this.age})" } }
|
次构造函数
Kotlin
在类中使用constructor
关键字声明次构造函数,可以有多个次构造函数,而无主构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
class User { private var name: String private var age: Int
constructor(name: String) : this(name, 18)
constructor(name: String, age: Int) { this.name = name this.age = age } }
|
当一个类既有主构造函数又有次构造函数时,所有次构造函数必须直接或间接的调用主构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class User() { private var name: String? = null private var age: Int? = null
constructor(name: String) : this(name, 18)
constructor(name: String, age: Int) : this() { this.name = name this.age = age } }
|
继承
Kotlin
中的类默认是不可继承的,如果要使一个类可以被继承,需要在类名前添加open
关键字,否则类和其中的方法都是final
的,无法被继承
类的继承使用:
符号,Kotlin
中所有类默认继承Any
类
1 2 3 4 5
| class IKun : Any()
class IKun() : Any()
|
被继承的类User
之所以带有()
,是因为Kotlin
和java
一样,在类继承时子类的构造函数会调用父类的构造函数,如果父类没有显式定义构造函数,子类继承时应该调用父类的无参构造函数,所以被继承的类带有()
如果父类有多个构造函数,子类继承时只需要选择调用一个构造函数即可,具体选择哪个,在继承时通过()
中的参数指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| open class User() { constructor(name: String) : this() { println("User的构造方法1") }
constructor(name: String, age: Int) : this(name) { println("User的构造方法2") } }
class IKun(name: String) : User()
class IKun(name: String) : User(name)
|
如果子类中只有次构造函数,没有主构造函数,那么继承父类时不能使用()
,而是在次构造函数中使用super
关键字调用父类的构造函数
1 2 3 4 5
| class IKun : User { constructor() : super() { println("IKun constructor") } }
|
数据类
Kotlin
中提供了数据类,使用data
关键字声明,会自动生成equals()
、hashCode()
、toString()
方法
1
| data class User(private val name: String, private val age: Int)
|
等同于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class User(private val name: String, private val age: Int) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is User) return false
if (age != other.age) return false if (name != other.name) return false
return true }
override fun hashCode(): Int { var result = age result = 31 * result + name.hashCode() return result }
override fun toString(): String { return "User(name='$name', age=$age)" } }
|
单例类
Kotlin
中提供了单例类,使用object
关键字声明,会自动生成一个实例,并且是线程安全的
1 2 3 4 5
| object Singleton { fun singletonTest() { println("singletonTest") } }
|
在其他kotlin
类中调用
1
| Singleton.singletonTest()
|
经过反编译发现,等同于
1 2 3 4 5 6 7 8 9
| class Singleton { companion object { var INSTANCE: Singleton = Singleton() }
fun singletonTest() { println("singletonTest") } }
|
在其他类中调用如下。其实如果一个使用object
关键字声明的类,在java
类中的调用也是用如下方式
1
| Singleton.INSTANCE.singletonTest()
|
接口
和java
一样,Kotlin
中的接口使用interface
关键字声明
1 2 3 4 5 6
| interface IKun { fun sing() fun dance() fun rap() fun basketball() }
|
实现接口使用:
符号,实现多个接口使用,
分隔,重写方法时,需要使用override
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class XiaoHeiZi : IKun { override fun sing() { println("我会唱") } override fun dance() { println("我会跳") } override fun rap() { println("我会rap") } override fun basketball() { println("我会篮球") } }
|
调用接口中的方法
1 2 3 4 5
| val iKun: IKun = XiaoHeiZi() iKun.sing() iKun.dance() iKun.rap() iKun.basketball()
|
静态
Lambda表达式
Lambda
表达式的结构如下
1 2 3 4 5 6 7
| { 参数名1: 参数类型, 参数名2: 参数类型 -> 函数体 }
val lambda = { s1: String, s2: String -> println("${s1},我是练习时长两年半的个人练习生cxk") println("${s2}唱跳rap篮球") }
|
函数式接口
函数式接口是指只包含一个抽象方法的接口,在Kotlin
中,函数式接口使用fun interface
关键字声明
Java
中的函数式接口使用@FunctionalInterface
注解声明
如果一个函数所需的参数是函数式接口
,那么可以直接将Lambda
表达式作为参数传递给函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
fun interface OnContentTransmitter {
fun transmitContent(content: String) }
class TransmitExecutor(private val onContentTransmitter: OnContentTransmitter) {
fun executeTransmit() { onContentTransmitter.transmitContent("hello world") } }
|
创建TransmitExecutor
对象时,需要传入OnContentTransmitter
的匿名类,如下
1 2 3 4 5
| TransmitExecutor (object : OnContentTransmitter { override fun transmitContent(content: String) { println(content) } }).executeTransmit()
|
使用Lambda
表达式可以简化代码
1 2 3 4
| val lambda = { content: String -> println(content) } TransmitExecutor(lambda).executeTransmit()
|
Lambda
表达式不必声明为变量,直接写入
1 2 3
| TransmitExecutor({ content: String -> println(content) }).executeTransmit()
|
Lambda
表达式的参数类型可以省略,编译器会自动推导
1 2 3
| TransmitExecutor({ content -> println(content) }).executeTransmit()
|
如果Lambda
表达式是最后一个参数,那么可以将其放在括号外面;如果Lambda
表达式是唯一一个参数,那么可以省略括号
1 2 3
| TransmitExecutor { content -> println(content) }.executeTransmit()
|
此外如果Lambda
表达式只有一个参数,参数可以使用it
关键字代替
1 2 3
| TransmitExecutor { println(it) }.executeTransmit()
|
同样如果一个函数需要的参数是函数式接口时,和上面的步骤一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
fun execute(onContentTransmitter: OnContentTransmitter) { onContentTransmitter.transmitContent("hello world") }
execute { println(it) }
|
函数作为参数
Lambda
表达式可以作为参数传递给函数,如下,在子线程中执行传入的函数
1 2 3 4
| fun execute(func: (content: String) -> Boolean) { val result: Boolean = func.invoke("hello world") println("执行结果 = $result") }
|
正常调用时
1 2 3 4 5 6
| execute (fun (content: String): Boolean { if (TextUtils.isEmpty(content)) return false println(content) return true })
|
通过Lambda
表达式简化代码
1 2 3 4 5 6
| execute { if (TextUtils.isEmpty(it)) return@execute false println(it) return@execute true }
|
函数作为返回值
Lambda
表达式也可以作为函数的返回值,如下函数的返回值也是函数,可以简化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
fun getFunc(): (name: String, like: String) -> Unit { return fun(name: String, like: String) { println("全民制作人大家好,我是练习时长两年半的个人练习生$name,我喜欢$like") } }
fun getFunc(): (name: String, like: String) -> Unit { return { name, like -> println("全民制作人大家好,我是练习时长两年半的个人练习生$name,我喜欢$like") } }
|
调用时
1 2 3
| getFunc().invoke("cxk", "唱、跳、rap、篮球")
getFunc()("cxk", "唱、跳、rap、篮球")
|