写的可能比较乱,仅在此记录一下方便以后查看
第一:
val result = when(类型){
匹配值 -> { 执行逻辑 }
is Int -> { is相当于java里面的instanceof关键字 }
else -> {都没有匹配上执行}
}
Kotlin中判断字符串或对象是否相等可以直接使用"=="关键字,不用使用像java那样调用equals()方法
val result = if(条件){ // 有返回值
逻辑相关1
}else{
逻辑相关2
}
for循环,iterator语法 continue与break也是可以使用的
for(i in 0..10){ // ..是0到10的闭区间的关键字 0和10包含在内
println(i)
}
// 使用until关键字创建一个左闭右开的区间
val range = 0 until 10
for (i in 0 until 10 step 2){
println(i)
}
// downTo降序关键字
for (i in 10 downTo 1 step 2){ // 10和1都会打出来
println(i)
}
// 遍历数组
val arr = intArrayOf(2, 4, 6, 8, 10)
// for (item: Int in arr) {
// println(item)
// }
// 使用索引遍历
// for (i in arr.indices) {
println(i)
// println("arr[$i] = " + arr[i])
// }
// 同时对索引和元素值进行循环
// for ((index, value) in arr.withIndex()) {
// println("arr[$index] = " + value)
// }
// 遍历集合
// val list = mutableListOf("Apple","Banana","Orange","Pear","Grape")
// list.add("Watermelon")
// for (fruit in list){
// println(fruit)
// }
// 遍历map
// val map = HashMap<String, Int>()
map.put("Apple", 1) // 不推荐这样使用
// map["Apple"] = 1
println(map["Apple"])
// map["Banana"] = 2
// map["Orange"] = 3
// map["Pear"] = 4
// map["Grape"] = 5
// val map = mutableMapOf("Apple" to 1,"Banana" to 2)
// map["Hello"] = 3
// for ((fruit,number) in map){
// println("fruit is " + fruit + ", number is " + number)
// }
构造函数
class Student(name: String, age: Int) : Person(name, age) {
// init { // 主构造函数初始化逻辑可以写在这里
// println("sno is " + sno)
// println("grade is " + grade)
// }
}
数据类
data class Cellphone(val brand: String, val price: Double) // 类中没有任何代码时可以将大括号省略
单例类
object Singleton {
fun singletonTest(){
println("singletonTest is called")
}
}
Lambda表达式
语法结构:{ 参数名1:参数类型,参数名2:参数类型 -> 函数体 }
// val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
// val maxLengthFruit = list.maxBy { it.length }
// 一步步进行推导
// val lambda = { fruit: String -> fruit.length }
// val maxLengthFruit = list.maxBy(lambda)
// val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length })
// 然后Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面
// val maxLengthFruit = list.maxBy() { fruit: String -> fruit.length }
// 如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略
// val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }
// 由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型,因此代码可以进一步简化成
// val maxLengthFruit = list.maxBy { fruit -> fruit.length }
// 最后,当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替,那么代码就变成了
// val maxLengthFruit = list.maxBy { it.length }
// 转换大写
// val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
// val newList = list.map { it.toUpperCase() }
// for (fruit in newList){
// println(fruit)
// }
// 过滤 保留5个字母以内的水果
// val newList = list.filter { it.length <= 5 }.map { it.toUpperCase() }
// for (fruit in newList) {
// println(fruit)
// }
Kotlin中创建匿名类实例需要使用object关键字
Thread(object : Runnable{
override fun run() {
TODO("not implemented")
}
}).start()
判空的辅助工具
?.当对象不为空时正常调用,为空时什么都不做 a?.test()
非空断言工具!! a!!.test() 确定a不为空 强行通过编译
?:操作符 左右两边都接收一个表达式,左边的表达式不为空时就返回左边表达式结果,否则就返回右边的结果 val c = a ?: b
let函数 会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了
fun doStudy(study: Study?) {
// study?.let { stu ->
// stu.readBooks()
// stu.doHomework()
// }
study?.let {
it.readBooks()
it.doHomework()
}
}
let函数是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身。
函数参数的默认值,可以通过键值对的方式来进行传值
printParams(str = "world",num = 123)
printParams(str = "c")
fun printParams(num: Int= 101, str: String) {
println("num is $num, str is $str")
}
第二:
val intent = Intent(this@FirstActivity, TwoActivity::class.java)
启动activity的最佳写法:
companion object {
fun actionStart(context: Context, data1: String, data2: String) {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra("param1", data1)
intent.putExtra("param2", data2)
context.startActivity(intent)
}
}
TwoActivity.actionStart(this,"data1","data2")
with函数的使用
// val result = with(StringBuilder()) { // lambda表达式中提供第一个参数的上下文
// append("Start eating fruits.\n")
// for (fruit in list) {
// append(fruit).append("\n")
// }
// append("Ate all fruits.")
// // 最后一行代码作为with函数返回值
// toString()
// }
// println(result)
run函数的使用(某个对象调用run函数)
// val result = StringBuilder().run {
// append("Start eating fruits.\n")
// for (fruit in list) {
// append(fruit).append("\n")
// }
// append("Ate all fruits")
// toString() // 最后一行代码作为run函数返回值
// }
// println(result)
apply函数的使用,无法指定返回值 会自动返回调用者对象本身
val result = StringBuilder().apply {
append("Start eating fruits.\n")
for (fruit in list) {
append(fruit).append("\n")
}
append("Ate all fruits.")
}
println(result.toString())
val intent = Intent(this@MainActivity, FirstActivity::class.java).apply {
putExtra("a","b")
// ......
}
startActivity(intent)
companion object 可以让类中的某一个方法变成类似静态方法的调用方式
companion object {
// 模仿静态方法的调用方式
fun doAction2() {
print("do action2")
}
// 真正的静态方法
@JvmStatic
fun doAction3() {
print("do action3")
}
}
object声明(一个类)是延迟加载的,只有当第一次被访问时才会初始化,所以被用来实现单例
companion object是当包含它的类被加载时就初始化了的,这一点和Java的static还是一样的
基本区别:
object 可以定义在全局也可以在类的内部使用
object 就是单例模式的化身
object 可以实现 Java 中的匿名类
companion object 就是 Java 中的 static 变量
companion object 只能定义在对应的类中
顶层方法 是没有定义在任何类里面的方法,Kotlin会把所有顶层方法都编译成静态方法
在使用java代码调用的时候需要使用HelperKt.doSomething(),Kotlin直接使用doSomething()调用就可以
fun doSomething(){
print("do something")
}
第三:
inner class xxx 关键字定义内部类
lateinit 延迟初始化关键字
// 判断对一个全局变量是否已经初始化过
if (!::adapter.isInitialized) { // 取反没有初始化进行初始化
adapter = MsgAdapter(msgList)
}
密封类sealed 的关键字及使用,可以省略else的分支写法,只能定义在同一文件的顶层位置
密封类作为条件使用时,会强制要求将每一个子类所对应的条件全部处理。
sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
//class Unknown(val test: String) : Result()
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
// is Unknown -> result.test
// else -> throw IllegalArgumentException()
}
// 在RecyclerView中的adapter的使用
sealed class MsgViewHolder(view: View) : RecyclerView.ViewHolder(view)
class LeftViewHolder(view: View) : MsgViewHolder(view) {
val leftMsg: TextView = view.findViewById(R.id.leftMsg)
}
class RightViewHolder(view: View) : MsgViewHolder(view) {
val rightMsg: TextView = view.findViewById(R.id.rightMsg)
}
override fun onBindViewHolder(holder: MsgViewHolder, position: Int) {
val msg = msgList[position]
when (holder) {
is LeftViewHolder -> holder.leftMsg.text = msg.content
is RightViewHolder -> holder.rightMsg.text = msg.content
// else -> throw IllegalArgumentException()
}
}
第四:
扩展函数的使用
fun ClassName.methodName(param1: Int, param2: Int) : Int {
return 0
}
fun String.lettersCount(): Int { // 判断字符串里有多少个字母
var count = 0
for (char in this) { // 函数中自动拥有String实例的上下文
if (char.isLetter()) {
count++
}
}
return count
}
operator 运算符重载关键字
class Money(val value: Int) {
operator fun plus(money: Money): Money {
val sum = value + money.value
return Money(sum)
}
/**
* 重载了plus函数
*/
operator fun plus(newValue: Int): Money {
val sum = value + newValue
return Money(sum)
}
}
// 两个Money相加及Money和数字相加
val money1 = Money(10)
val money2 = Money(5)
val money3 = money1 + money2
val money4 = money3 + 20
println(money4.value)
a in b --- b.contains(a) // a是否在b中 b是否包含a
if("hello".contains("he")){
}
if("he" in "hello"){
}
第五:
高阶函数-可以定义自己的函数式API
定义规则 (String,Int) -> Unit 左边接收什么参数 右边是返回值的类型
fun example(func: (String, Int) -> Unit) {
func("hello",123)
}
// 定义一个高价函数
// inline 内联函数 使用高阶函数时,每调用一次Lambda表达式都会创建一个新的匿名类实例,会造成额外的内存和性能开销,所以在定义高阶函数时使用内联函数声明,编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样就不存在运行时的开销了。
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
val result = operation(num1, num2)
return result
}
// 调用高阶函数
val num1 = 100
val num2 = 90
// val result1 = num1AndNum2(num1, num2) { n1, n2 ->
// n1 + n2
// }
// val result2 = num1AndNum2(num1, num2) { n1, n2 ->
// n1 - n2
// }
// println("result1 is $result1")
// println("result2 is $result2")
/**
* 高阶函数
* 在函数类型的前面加上StringBuilder.就表示这个函数类型是定义在哪个类当中的
* ClassName.是定义高阶函数完整的语法规则
*/
fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
block()
return this
}
// val list = listOf("Apple", "Banana")
// val result = StringBuilder().build {
// append("Start eating fruits.\n")
// for (fruit in list) {
// append(fruit).append("\n")
// }
// append("Ate all fruits")
// }
// println(result.toString())
noinline 非内联函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实的函数,而内联函数类型参数只允许传递给另外一个内联函数;内联函数所引用的Lambda表达式中是可以使用return关键字来进行函数返回的,而非内联函数只能进行局部的返回且Lambda表达式中是不能直接使用return关键字的,可以使用return@printString表示进行局部返回,不在执行Lambda表达式中剩余的部分代码。
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {
}
在高阶函数中创建了另外的Lambda或者匿名内部类的实现,并且在这些实现中调用函数类型参数,此时再将高阶函数声明成内联函数就会出现错误,这时候就需要借助crossinline关键字来消除错误,主要是因为内联函数的Lambda表达式中允许使用return关键字和高阶函数的匿名类实现中不允许使用return关键字之间造成了冲突,而crossinline关键字就像一个契约,保证在内联函数的Lambda表达式中一定不会使用return关键字,这样冲突就不存在了。
inline fun runRunnable(crossinline block: () -> Unit) {
val runnable = Runnable {
block()
}
runnable.run()
}
use函数在Lambda表达式中的代码全部执行完之后自动将外层的流关闭,不需要写finally语句
forEachLine函数会将读到的每行内容都回调到Lambda表达式中,在Lambda表达式中完成拼接逻辑即可。
vararg pairs 关键字对应的就是java中的可变参数列表...
by 委托关键字
by lazy是通过属性代理来实现的懒加载,只在第一次使用的时候才会去执行表达式,并且只会执行一次。
by lazy默认是线程安全的,内部通过双重判断锁来保证只执行一次代码块赋值
当能够确定不会发生在多线程中的时候,可通过lazy(LazyThreadSafetyMode.NONE) { ... }来避免加锁。
infix函数 一种高级语法糖,可以构建更可读的语法。
infix函数不能定义成顶层函数,必须是某个类的成员函数且函数只能接收一个参数(参数类型没有限制),可以使用扩展函数的方式将它定义到某个类当中。
if("Hello Kotlin".startsWith("Hello")){
}
infix fun String.beginsWith(prefix: String) = startsWith(prefix)
if("Hello Kotlin" beginsWith "Hello"){ // 可以使用这种方式来调用
}
infix fun <T> Collection<T>.has(element: T) = contains(element)
val list = listOf("Banana","Apple")
if (list.contains("Banana")){
}
if (list has "Banana"){
}
第六:
泛型实化,可以写T::class.java这样语法,首先必须使用inline关键字声明为内联函数 然后声明泛型的地方必须加上reified关键字来表示该泛型要进行实化。
inline fun <reified T> startActivity(context: Context) {
val intent = Intent(context, T::class.java)
context.startActivity(intent)
}
inline fun <reified T> startActivity(context: Context, block: Intent.() -> Unit) {
val intent = Intent(context, T::class.java)
intent.block()
context.startActivity(intent)
}
// 调用方式
startActivity<TestActivity>(this)
startActivity<TestActivity>(this) {
putExtra("param1", "测试")
putExtra("param2", 1)
}
协程:主要用于处理高并发的场景
suspend 能将一个函数声明成挂起函数,但是无法给它提供协程作用域。
//suspend fun printDot(){
// println(.)
// delay(1000)
//}
coroutineScope是一个挂起函数,因此可以在任何其他挂起函数中调用,会继承外部的协程作用域并创建一个子作用域,可以给任意挂起函数提供协程作用域,保证其作用域内的所有代码和子协程在全部执行完之前会一直阻塞当前协程。
suspend fun printDot() = coroutineScope {
launch {
println(".")
delay(1000)
}
}
// 协程实际项目中建议的使用
// val job = Job()
// val scope = CoroutineScope(job) // CoroutineScope这是一个函数
// scope.launch {
// // 处理具体的逻辑
// }
// scope.cancel()
使用async函数创建一个协程并获取执行结果,async函数必须在协程作用域当中才能调用,会创建一个子协程并返回一个Deferred对象,获取执行结果调用Deferred对象的await()方法,当调用了async函数后,代码块中的代码会立刻执行,当调用await()函数时。如果代码块中的代码还没执行完,那么await()函数会将当前协程阻塞住,直到可以获得async函数的执行结果为止。
// runBlocking {
// val result = async {
// 5+5
// }.await()
// println(result)
// }
// 例子证实
// runBlocking {
// val start = System.currentTimeMillis()
// val deferred1 = async {
// delay(1000)
// 5 + 5
// }
// val deferred2 = async {
// delay(1000)
// 4 + 6
// }
// println("result is ${deferred1.await() + deferred2.await()}.")
// val end = System.currentTimeMillis()
// println("cost ${end - start} ms.")
// }
withContext()函数可以理解为async函数的一种简化版写法,调用withContext()函数后会立即执行代码块中的代码同时会将当前协程阻塞住,会将最后一行代码执行结果作为返回值。
Dispatchers.Default 默认低并发的线程策略,Dispatchers.IO 较高并发的线程策略(网络请求可以使用),Dispatchers.Main 在Android主线程中执行代码,只能在Android项目中使用。
runBlocking {
val result = withContext(Dispatchers.Default) {
5 + 5
}
println(result)
}
/**
* suspendCoroutine函数必须在协程作用域或挂起函数中才能调用,接收一个Lambda表达式参数,主要是将当前协程立即挂起
* 然后在一个普通线程中执行Lambda表达式中的代码 表达式参数列表会传入一个Continuation参数 调用它的resume()方法或resumeWithException()方法可以让协程继续(huifu)执行
*/