Kotlin筑基

2023-11-11

Kotlin筑基

本文链接

核心思路:每个知识点都要和源码结合起来讲。

文章目录

编译时常量

1、const val PI = 45 编译时常量不可以用于局部变量,为什么?

函数之内必须在运行时赋值,不符合编译时常量

2、编译时常量(compile-time constants)有什么用?

  1. 提高性能:编译时进行常量折叠(constant folding),避免在运行时进行重复计算。
  2. 减少错误和提高可读性:减少人为错误的可能性,会在编译时验证。可以提高代码的可读性和可维护性。
  3. 编译时配置:定义应用程序的版本号、构建类型或其他配置参数,从而在编译期间对应用程序进行不同的处理。
  4. 优化资源使用:可以在代码中指定资源的路径,避免在运行时进行资源查找和加载的开销。

基本类型

3、Kotlin基本类型也是引用类型,会不会耗费性能?不会,都转为基本类型了

range

4、range表达式

n in 0..10
n !in 0..10

访问权修饰符

5、函数默认public,其他修饰符

  1. private、protected
  2. internal:同一module可见
  3. protected internal:同一模块或者子类可见
  4. internal可以提高代码封装性

Unit

6、默认返回Unit可以不写,kt中Unit是单例类

Nothing

7、Nothing类型是什么?

  1. TODO(“抛出异常,结束当前程序”),这不是注释
  2. TODO()返回的类型是Nothing

反引号

8、反引号是什么?

private fun `登录功能20230727环境测试功能`(name:String){
    println(name)
}

9、kt中in和is是关键字,想要函数名为in,is怎么办?反引号

fun `in`(){}
`in`() // 调用

10、反引号可以用于函数加密,公司内部有文档

private fun `9867693746293234`(name:String){
    // xxx
}

函数内联

11、函数内联 - 有lambda作为参数就需要内联inline

  1. 不内联,在调用端会生成函数对象(Function2等)来调用
  2. 使用内联,不会有额外性能损耗,但是代码量很大的函数复制多处会导致代码体积增大
  3. 内联相当于C++的#define 宏替换
fun main(args: Array<String>) {
    login("wch"){
        println(it)
    }
}

// 此处lambda是一个参数,因此生成Function1的对象
// (String,String)-> Unit 会生成Function2
inline fun login(name:String, lambda:(String)->Unit){
    lambda(name)
}

函数引用

12、函数引用的例子

fun main(args: Array<String>) {
    var method = ::login
    method("wch"){
        println(it)
    }
}

inline fun login(name:String, lambda:(String)->Unit){
    lambda(name)
}

13、函数可以作为返回值,有什么用?

  1. 延迟计算
fun calculateResult(): () -> Int {
    val result = 10 // 假设这是一个复杂的计算过程
    return { result }
}

val delayedResult = calculateResult()
val result = delayedResult() // 只有在需要时才执行计算
  1. 策略模式
enum class SortOrder {
    ASCENDING, DESCENDING
}

fun getSortComparator(order: SortOrder): (List<Int>) -> List<Int> {
    return when (order) {
        SortOrder.ASCENDING -> { list -> list.sorted() }
        SortOrder.DESCENDING -> { list -> list.sortedDescending() }
    }
}

val numbers = listOf(4, 2, 7, 1, 5)
val sortOrder = SortOrder.ASCENDING
val sortedNumbers = getSortComparator(sortOrder)(numbers) // 根据排序顺序返回不同的函数实现
  1. 装饰器模式
fun performOperation(): () -> Unit {
    return {
        // 执行原始操作
        println("Performing the original operation")
    }
}

fun withLogging(originalFunction: () -> Unit): () -> Unit {
    return {
        println("Logging before operation")
        originalFunction()
        println("Logging after operation")
    }
}

val operation = performOperation()
val decoratedOperation = withLogging(operation) // 包装原始函数,添加日志记录的功能
decoratedOperation() // 执行装饰后的操作,会在控制台输出相关日志

具名函数

14、具名函数有什么用?

fun printResult(result:String){
    println(result)
}
login("wch", ::printResult) //用符号引用调用具名函数

判空和安全调用

15、安全调用操作符

    var name: String? = "feather"
    name = null
    name?.capitalize()

!!

确保是有数值的才能用,这个和java一样,有风险

16、let的安全调用

    name?.let{
        it.xxx() //不为null才会执行
    }

断言操作、空合并

17、非空断言、if判断

  1. 为null也会执行,和Java一样。百分百确定有值,可以使用断言。
name!!.capitalize()
//or
if(name != null){
   name.capitalize() 
}

18、空合并 ?:

var name:String? = "李小龙"
name = null
println(name ?: "原来你是鼎鼎大名null")

// ?: 前面为空,才执行
// 如果name = null会执行 ?: 后面的

let + ?:

//let + ?:
var name:String? = "李小龙"
name = null
println(name?.let { "我的名字是$it" } ?: "原来我是鼎鼎大名null")

异常处理

19、自定义异常

fun main(args: Array<String>) {
    throw CustomException()
}
// 自定义异常
class CustomException : IllegalArgumentException("illegal")

先决条件

checkNotNull(value)
requireNotNull(value)

var value = false
require(value) // false 会抛出异常

subString

subString(0, index)
subString(0 until 10)
// 两者等价

split

val list = text.split(",") // 分割成List
println(list)
// C++ 和 Kt都有解构
val (v1, v2, v3, v4) = list

// 不接受第一个数据,反编译不会get(0),节省性能
val (_, v2, v3, v4) = list

解构

在 Kotlin 中,解构(Destructuring)是一种将复合数据结构(如类、数据类、数组、集合等)的多个成员拆分为单个变量的技术。:

  1. 解构声明:解构一个数据类的属性:
data class Point(val x: Int, val y: Int)

val point = Point(10, 20)
val (x, y) = point // 解构 Point 对象为两个独立的变量
println("x: $x, y: $y") // 输出 x: 10, y: 20
  1. 解构函数返回值:一个函数返回多个值:
fun getUser(): Pair<String, Int> {
    return Pair("John Doe", 25)
}

val (name, age) = getUser() // 解构函数返回的 Pair 对象
println("Name: $name, Age: $age") // 输出 Name: John Doe, Age: 25
  1. 解构数组和集合:对于数组和集合,解构可以将其元素拆分为单独的变量。
val numbers = arrayOf(1, 2, 3, 4, 5)
val (first, second, *rest) = numbers // 解构数组元素为单独的变量
println("First: $first, Second: $second, Rest: $rest") // 输出 First: 1, Second: 2, Rest: [3, 4, 5]

val list = listOf("apple", "banana", "cherry")
val (fruit1, fruit2) = list // 解构列表元素为单独的变量
println("Fruit 1: $fruit1, Fruit 2: $fruit2") // 输出 Fruit 1: apple, Fruit 2: banana
高级技巧
  1. 解构 lambda 参数:我们可以在 lambda 表达式中使用解构来获取函数参数的多个部分。例如:
val person = mapOf("name" to "John", "age" to 30)

person.forEach { (key, value) ->
    println("Key: $key, Value: $value")
}
  1. 解构过滤和映射:我们可以在解构过滤器和映射函数中使用解构来处理集合元素。例如:
data class Product(val name: String, val price: Double)

val products = listOf(
    Product("Apple", 1.99),
    Product("Banana", 0.99),
    Product("Cherry", 2.49)
)

val (expensiveProducts, cheapProducts) = products.partition { (_, price) -> price > 1.0 }

println("Expensive Products: $expensiveProducts")
println("Cheap Products: $cheapProducts")
  1. 解构数据结构嵌套:如果数据结构嵌套,我们可以使用嵌套的解构来访问内部对象的属性。例如:
data class Person(val name: String, val age: Int, val address: Address)
data class Address(val street: String, val city: String)

val person = Person("John Doe", 30, Address("123 Main St", "City"))

val (name, _, (street, city)) = person

println("Name: $name")
println("Street: $street, City: $city")
partition

partition 是用于将集合元素拆分为满足某个条件和不满足该条件的两个集合的函数。它返回一个包含两个集合的 Pair 对象,其中一个集合包含满足条件的元素,另一个集合包含不满足条件的元素。

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

val (evenNumbers, oddNumbers) = numbers.partition { it % 2 == 0 }

println("Even Numbers: $evenNumbers") 
println("Odd Numbers: $oddNumbers")

输出结果为:
Even Numbers: [2, 4, 6, 8, 10]
Odd Numbers: [1, 3, 5, 7, 9]

replace

val r1 = sourcePwd.replace(Regex("[AKMNO]")){ //Regex 中 写正则表达式
    it.value // 啥也没做
    // 可以加逻辑,把AKMNO替换成需要的内容
}

// 用正则表达式Regex进行替换,可以加密和解密
val newName = name.replace(Regex("[ADG]")){
    when(it.value){
        "A"->"1"
        "D"->"2"
        "G"->"3"
        else->it.value
    }
}

== 和 === 区别

== 内容的比较,等价于 equals
=== 引用的比较
val name1 = "Derry"
val name2 = "Derry"
name1 === name2 
结果:
true // 字符串常量池同一个,是true
val name1 = "Derry"
val name2 = "derry".capitalize() // "Derry"
name1 === name2 
结果:
false // name2会生成另一个Derry,和常量池原先的Derry是两个对象

数字安全转换

val number:Int = "666".toInt()
val number:Int = "666.6f".toInt() // 会报错
val number:Int? = "666.6f".toIntOrNull() // 不会报错为Null

Double 转为 Int

64.55.toInt() // 四舍五入
64.55.roundToInt() // 四舍五入

// 保留小数点
"%.3f".format(65.742355) // %是格式化字符串的标志,%.3f小数点后三位

内置函数

apply

"feather".apply{
    // 直接this拿到 feather,可以直接调用 String的方法
    toLowerCase()
    this[length - 1]
}

适合链式调用: 例如文件解析

file.apply{
    setExecutable(true)
}.apply{
    setReadable(true)
}.apply{
    // xxx
}

apply设置对象的成员变量,run也可以这么做(都有this)

class Configuration {
    var host: String = ""
    var port: Int = 0
    var timeout: Int = 0
}

val config = Configuration().apply {
    host = "example.com"
    port = 8080
    timeout = 5000
}

apply源码:T.() 拥有this

public inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this // return this,用于链式调用
}

let

let源码: (T) 拥有it

public inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

使用:

val r = a?.let {
     // 能执行到这里it一定不为null
    if(it.isBlank()){
        "DEFAULT"
    }else{
        it
    }
}

run

info.run{ // this
    "hello" // 最后一行返回
}

run的源码: T.() 拥有this

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

with

with和run的使用方法不同,其他一模一样

with("wch"){      
}
// T.() 拥有this
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}
run和with区别
  1. with适用于在特定上下文对对象进行操作
  2. run适合链式操作

also

also源码:(T) 拥有it

public inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

takeif

true时,返回对象本身,否则返回null

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    return if (predicate(this)) this else null
}

takeUnless

false时,返回对象本身,否则返回null

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    return if (!predicate(this)) this else null
}

总结

let = block(this) // it
apply block() = this   // this // 返回自身,链式
also block(this) = this // it // 返回自身,链式
with = receiver(this)  // this
run = block() // this
takeIf = precidate(this) // it
takeUnless = !precidate(this) // it

集合

listOf

list的注意点

  1. listOf(xxx)可以获取list
  2. list[0] 正常取值
  3. kt特点:取值避免异常
    list.getOrElse(index){"数组越界了"}
    list.getOrNull(index)

为什么是不可变的?==> 协变

List<out E>
是生产者,只能取数值。

mutableListOf

val list = mutableListOf(1, 2, 3, 4, 5)
list.toList() // 转为不可变集合

val list = listOf(1, 2, 3, 4, 5)
list.toMutableList() // 转为可变集合

list += 99 // 运算符重载,+= -=
list.removeIf{ true } // 自动遍历全部移除
list.removeIf { it%2 == 0 } // 移除符合条件的数据

遍历

for(i in list){
    //111
}
list.forEach{
    //222
}
// 第三种方式
list.forEachIndexed{ index: Int, item: Int ->  
    print("$index $item,")
}

Set:元素不重复

val set = setOf("a", "cd", "xsw")
set.elementAt(3) // 可能越界
set.elementAtOrElse(3, {"Set越界咯"})
set.elementAtOrNull(3)
// 配合 空合并
println(set.elementAtOrNull(3) ?: "为null")
// 可变Set
val set = mutableSetOf<String>("a", "cd", "xsw")

集合转换(去重)、快捷函数

val list = mutableSetOf<String>("a", "cd", "xsw")
list.toSet() // 去重
list.toSet().toList() // 去重
list.distinct() // 快捷去重,内部先转为Set再转为List

数组

数组的创建

  1. intArrayOf,xxx,arrayOf 对象数组
  2. list.toCharArray list转为数组

Map

创建、读取、遍历

    // 创建Map,两者等价
    val map = mapOf("Derry" to (13.4), "wch" to (38.9)) // 前者key,后者value
    val map2 = mapOf(Pair("wch", 20), Pair("feather", 37)) // 源码中

    // 读取Map,五中方式
    map["wch"] // kt方式,返回null
    map.get("xwd") // java方式,返回null
    map.getOrDefault("feather", 37.8) // 默认值保护
    map.getOrElse("khw") { 98.2 }
    map.getValue("zas") // 会崩溃!!!不要用

    // 遍历
    // 第一种
    map.forEach{ // it = Entry
        it.key
        it.value
    }
    // 第二种
    map.forEach{ key,value -> println("$key $value") }
    // 第三种: 解构
    map.forEach{ (k,v) -> println("$k $v") }
    // 第四种:in
    for(item in map){

    }

可变Map: getOrPut

// 存在取出,不存在存入
    val map = mutableMapOf("Derry" to (13.4), "wch" to (38.9)) // 前者key,后者value
    map.getOrPut("fff") { 10.9 }

1、类中的字段默认的访问权限修饰符是什么?为什么外界可以访问?

  1. 默认是private
  2. 提供了set和get
class MyKt{
    var name = "wch"
        get() = field // 默认有get
        set(value) { // 默认有set
            field = value
        }
}

计算属性

// 在使用时随机获取了值
    val name:Int // field已经失效了 // 没有name这个成员变量了
        get() = (1..1000).shuffled().first() // 随机排序后,取第一个数据
// int getName(){return (1..1000).shuffled().first()}

防范竞态条件

var name:String ? = null
  • 以后使用name时,都要判断name是否为空

主构造函数

class MyKt(){ // MyKt()主构造函数,可以不写
}

// _name _age 都是临时输入类型,不可以直接使用
class MyKt(_name:String, _age:Int){
}

// 使用输入数据一
class MyKt(_name:String, _age:Int){
    val name = _name
    // 可以额外修改
    get() = "My name is $_name"
}
// 使用输入数据二
class MyKt(val name:String, _age:Int){
}
  • get 不允许私有化

次构造

    // 次构造,必须调用主构造
    constructor(name: String, sex: Char):this(name){

    }

构造函数:默认参数

1、所有的构造函数都是用了默认参数,调用MyKt()应该是哪个构造函数?

  • 主构造

init代码块

  1. 可以使用临时参数
  2. 可以require检查合法性
  3. init代码块前后代码按顺序合并

懒加载 lateinit

  1. lateinit懒加载,未初始化时判空和使用都会抛出异常
  2. 需要用::name.isInitialized 进行检查
  3. 需要手动加载!
class MyKt{
    lateinit var name:String
    fun initName(){
        name = "feather"
    }
    fun getNameUnsafe(){
        if(name != null){
            println(name)
        }
    }
    fun getNameSage(){
        if(::name.isInitialized){
            println(name)
        }else{
            println("你还没有初始化哦")
        }
    }
}

惰性初始化 by lazy

  1. 自动加载,使用到的时候加载。
class MyKt{
    // 普通方式,非懒加载
    val name:String = requestName()
    // 懒加载
    val name2  by lazy { requestName() }
    fun requestName() = "feather"
}

初始化陷阱

1、init代码块前后具有顺序

// Error!
init{
    name = "feather"
}
var name:String
  1. init代码中变量初始化前,使用变量,后面调用getNameInfo()报错
class MyKt{
    val name:String
    init{
        getNameInfo()
        name = "feather"
    }
    fun getNameInfo() {
        println("${name[0]}")
    }
}
fun main() {
    MyKt().getNameInfo()
}
  1. 对象构造时,非空成员变量,利用调用方法饿汉加载,导致拿到了null。修改方法:临时参数赋值到成员变量要及时。
// 1. 主构造函数
class MyKt(_name:String){
    // 2. info = name = null // 一定拿到null
    val info = getNameInfo()
    // 3. name才赋值
    val name = _name
    fun getNameInfo() = name
}
fun main() {
    MyKt("feather").info.length
}

open

  1. Kt中所有类默认final不允许继承,需要open
  2. Kt中所有方法默认final,需要open才可以overide(重写)
  3. Kt中类主构造方法中成员变量如果可以被overide也需要open。或者用不同名字。
open class Peron(open val name:String){
    open fun printName(){}
}
class Student(override val name:String):Peron(name){
    override fun printName(){}
}

类型转换

  1. is 可以检测类型
  2. as 转换
if(p is Student){
    p as Student
}

智能类型转换

val p:Person = Student("wch")
// is后自动转换,可以直接调用
if(p is Student)
{
    p.printStudentName()
}

Any

1、Any是什么?相当于Object

  1. 所有类都隐式继承Any
  2. Any有equals、hashCode、toString方法,但都看不到实现 ==> Java Object有
  3. Any交到不同平台有不同实现:Linux、Windows等等,只提供了标准

对象声明,object

1、object是什么有什么用?

  1. 关键字,既是单例的对象又是类名
  2. MyKt可以作为类名,也可以作为单例对象
object MyKt{
    init {
        println("init初始化")
    }
}
// 单例类的内部默认生成了INSTANCE,调用show()等价于
// MyKt.INSTANCE.show()
MyKt.show()

MyKt生成的代码如下:

  1. object的init代码生成在static代码块中
  2. 正常class的init代码生成在主构造器中。
// final类
// INSTANCE在static时初始化,
public final class MyKt {
    @NotNull
    public static final MyKt INSTANCE;
    public final void show() {}
    private MyKt() {}
    static {
        MyKt var0 = new MyKt();
        INSTANCE = var0; // 唯一
        String var1 = "init初始化";
        System.out.println(var1);
    }
}

匿名对象

  1. java接口:匿名对象和lambda 两种方式
  2. kt接口:匿名对象 一种
// 具名对象,接收匿名对象
val obj = object : OnDialogClickListener {
    override fun onCancelClick() {
        TODO("Not yet implemented")
    }
    override fun onConfimrClick() {
        TODO("Not yet implemented")
    }
}
// Runnable
val obj = object : Runnable {
    override fun run() {
        TODO("Not yet implemented")
    }
}
// lambda方式 - 一个方法的函数式接口
val obj = Runnable {
    println("lambda方式")
}

伴生对象

1、为什么会有伴生对象?

  1. 在Kotlin中没有类似Java的static
    2、为什么伴生对象的字段可以直接访问?
  2. 背后代码 MyKt.Companion类 访问的info是MyKt的static字段info,info是private的。但是通过Companion可以访问
  3. 伴生对象永远只会初始化一次
class MyKt{
    companion object{
        const val info = "info"
    }
}
MyKt.info

嵌套类和内部类

1、Kt中默认内部类不可以访问外部类,需要增加修饰符 => 默认是嵌套类

  1. 需要加上inner修饰符
class MyKt{
    inner class InnerClass{}
}

数据类

1、数据类的定义,和普通类的区别是什么?

  1. 数据类内部会增加:set、get、构造、解构函数、copy、toString、hashCode
  2. 普通类 == ,内容一样会认为是两个对象。数据类 == 可以正确判断。
// 普通类 只要get、set
class ResponseBean(var code:Int, var msg:String, var data:String)
// 数据类
data class ResponseBean2(var code:Int, var msg:String, var data:String)

2、数据类使用场景和注意点?

  1. 网络请求返回数据时用
  2. 需要比较、copy、toString、解构时用
  3. 必须要有主构造函数,必须要有参数
  4. 不可以abstract、sealed、open、inner 专注于数据

copy

  1. copy方法主管主构造

解构

自定义解构功能:

  1. 需要operator 运算符重载
  2. 需要名字严格按照component1增加
// 普通类
class ResponseBean(var code:Int, var msg:String, var data:String){
    operator fun component1() = code
    operator fun component2() = msg
    operator fun component3() = data
    // xxx
}

运算符重载

operator进行重载

data class Data(var number1:Int, var number2:Int){
    operator fun plus(data: Data): Data {
        number1 += data.number1
        number2 += data.number2
        return this
    }
}
fun main() {
// C++中重载 + - 就可以了
// Kt中需要关键字 plus
    val result = Data(1, 1) + Data(2, 2)
    println(result)
}

查看Kt支持的所有运算符:点号,IDE会有提示

operator fun Data.xx()

枚举类

1、Kt中枚举类为什么和Java不同还有个class?

  1. 想要表达和Java的不同,枚举也是类
// 1、基本的枚举类
enum class Week{
    星期一,
    星期二,
    星期三,
    星期四,
    星期五,
    星期六,
    星期日
}
fun main() {
    println(Week.星期一)
    println(Week.星期日)
    // 枚举的值 等价于 枚举本身
    println(Week.星期四 is Week)
}

2、枚举类中传入对象+定义方法

// 枚举类中存储对象
class Weather(val info:String)
enum class Week(var weather: Weather){
    星期一(Weather("晴朗")),
    星期二(Weather("小雨")),
    星期三(Weather("中雨")),
    星期四(Weather("暴雨")),
    星期五(Weather("阴")),
    星期六(Weather("雨夹雪")),
    星期日(Weather("大雪"));
    // 枚举中定义方法
    fun show() = println("$name:${weather.info}")
    // 更新属性值
    fun updateWeather(weather: Weather){
    this.weather = weather
}
}
fun main() {
    println(Week.星期日)
    println(Week.星期一.weather.info)
    Week.星期四.show()
    Week.星期四.updateWeather(Weather("大雪"))
    Week.星期四.show()
}

3、枚举类中定义抽象方法

  1. 方法和枚举之间需要封号;
enum class Week {
    星期一 {
        override fun printInfo() {
            println(this.name)
        }
    },
    星期二 {
        override fun printInfo() {
            println(this.name)
        }
    };
    abstract fun printInfo()
}
fun main() {
    var day = Week.星期一
    day.printInfo()
    day = Week.星期二
    day.printInfo()
}

代数数据类型

什么是代数数据类型?

when配合枚举,数据非常明确,不需要else

密封类

1、密封类是什么?

  1. 一种特殊的类,用于表示有限的类继承结构。
  2. 用于限制类的继承,并且只允许定义一些特定的子类。
  3. 可以对密封类的子类做模式匹配,不需要else。
sealed class Result {
    class Success(val data: String) : Result()
    class Error(val message: String) : Result()
}
// 密封类的子类进行模式匹配,使用 `when` 表达式来处理不同的子类情况
fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println("Success: ${result.data}")
        is Result.Error -> println("Error: ${result.message}")
    }
}

2、密封类的使用例子

  1. 表示不同类型的网络请求结果:
sealed class NetworkResult {
    class Success(val data: String) : NetworkResult()
    class Error(val message: String) : NetworkResult()
    object Loading : NetworkResult()
}
fun handleNetworkResponse(result: NetworkResult) {
    when (result) {
        is NetworkResult.Success -> println("成功: ${result.data}")
        is NetworkResult.Error -> println("错误: ${result.message}")
        NetworkResult.Loading -> println("加载中...")
    }
}
  1. 表示不同类型的颜色:
sealed class Color {
    object Red : Color()
    object Green : Color()
    object Blue : Color()
    class Custom(val hexCode: String) : Color()
}
fun printColor(color: Color) {
    when (color) {
        Color.Red -> println("红色")
        Color.Green -> println("绿色")
        Color.Blue -> println("蓝色")
        is Color.Custom -> println("自定义颜色: ${color.hexCode}")
    }
}

3、密封类中object和class的区别

  1. 密封类只允许两种子类object和class
  2. object,没有自己的属性,单例对象,始终如一
  3. class,可以为子类创建实例,并且传入属性值

接口和默认实现

  1. interface关键字
  2. 接口是可以通过get()进行动态实现的,但是没有实际价值

抽象类

abstract class 抽象类

泛型类

class KtClass<T>{
    xxx
}

vararg 动态参数

// 动态参数的泛型,可以传入null
class MyKt<T>(vararg objects: T) {
    // 用于输出的T,生产者,只读。---规定。
    val objectArray: Array<out T> = objects
}

out in

为什么需要协变和逆变?

默认,List和List是两个类型

// out 生产者,只可以作为返回值
interface Producer<out T>{
    fun producer() : T
   // fun comsume(item : T)
}
// out
// List<Person> = List<Man>

// in 生产者,可以修改值,但不可以作为方法返回出去
interface Comsumer<in T>{
    fun comsume(item : T)
   // fun producer() : T
}
// List<Person> = List<Animal>
  • 默认,泛型子类对象,不可以赋值给泛型的父类引用

类似Java中,List和List是两个类型

  • out,泛型的子类对象可以赋值给泛型的父类引用
  • int, 泛型的父类对象可以赋值给泛型的子类引用

应用

什么时候需要out 和 in?

  1. 想要保护数据不被修改,用out T
  2. 只能修改,不允许获取 in T。指的是不能够把数据作为方法返回值,提供出去。
  3. Kotlin允许声明处泛型,

reified

1、reified的作用是什么?
reified关键字用于在具有inline函数修饰符的函数中获取“泛型参数的实际类型”。以下是使用reified的基本语法:

inline fun <reified T> functionName() {
    // 在函数体中可以使用 T 的实际类型
    T::class.java
}

2、要使用reified关键字,需要注意以下几点:

  1. reified只能在具有inline修饰符的函数中使用,因为它需要在编译时对函数的字节码进行改变,并保留类型信息。
  2. reified只能用于泛型类型参数,即在尖括号< >中的泛型参数前面使用reified关键字。
  3. 在使用reified的函数体内,可以直接使用泛型类型参数T的实际类型,如T::classT::class.java等。

3、让is和T配合使用

class Person(val name: String)
class Producer{
    inline fun<reified T> test(obj: Person){
        obj.takeIf { it is T } as T
    }
}

扩展函数

扩展方法:增强类、第三方库等类

class MyKt(val name:String)
fun MyKt.showName(){
    println("name:$name")
}
// 扩展函数生成的代码
// 调用时会自动把对象传入该方法,进行调用
public static final void show(MyKt $this$show){ 
    System.out.println("name:" + $this$show.getName());
}

扩展同名函数会覆盖原先的函数,子类(Int)的扩展函数优先于父类(Any)的扩展函数

超类扩展函数

1、对Any类扩展会导致所有类都有该方法

  1. 影响范围大
  2. 可以链式调用

内置函数源码解析:let

let源码:

@kotlin.internal.InlineOnly // 注解主要用于标记 Kotlin 内部的函数,以指示编译器将函数的实现内联到调用点
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

// 最后一行
public inline fun <T, R> T.let(block: (T) -> R): R { // it,最后一行作为返回值
    return block(this)
}
public inline fun <T,R> with(receiver:T, block: T.() -> R):R{ // this,最后一行
    return receiver.block()
}
// 自身
public inline fun <T> T.apply(block: T.() -> Unit): T { // this,自身(链式调用)
    block()
    return this
}
public inline fun <T> T.also(block: (T) -> Unit): T{ // it,自身(链式调用)
    block(this)
    return this
}
// it,自身或者null
public inline fun <T> T.takeIf(predicate: (T)->Boolean): T?{ // it,自身或者null
    return if(predicate(this)) this else null
}
public inline fun <T> T.takeUnless(predicate: (T)->Boolean): T?{ // it,自身或者null
    return if(!predicate(this)) this else null
}

扩展属性

扩展属性:val+get()只读不可以修改

val String.info
    get() = "MyInfo"

生成的Java代码:

   @NotNull
   public static final String getInfo(@NotNull String $this$info) {
      Intrinsics.checkNotNullParameter($this$info, "$this$info");
      return "MyInfo";
   }

infix 中缀表达式

infix是什么?有什么好处?

  1. infix代表中缀表达式,模仿C++。支持"wch" to 10的效果,而不需要"wch".to(10)
val map = mapOf("wch" to 10, "cyz" to 20)
println(map)
  1. 自定义中缀表达式
// 需要成为A的扩展函数
public infix fun<A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

扩展文件:将相关扩展函数写到一个文件中,需要时引入文件。

重命名

将方法名重命名:缩减代码量

import kotlin.let as l
fun main() {
    "feather".l {
        println("it = $it")
    }
}

// 第二个例子:短了很多
import com.personal.tax.compose.IWantToDoMyLicense as p
fun IWantToDoMyLicense(){
    println("Hello World!")
}
fun main() {
    p()
}

map

  1. map{it == 每一个元素} 元素类型 String Int Boolean等等,最终返回集合List<XXX>
    val list = listOf("wch", "cyz", "xxx")
    println(list)
    // 经过变化后,最后一行,作为新元素加入新集合
    var list2 = list.map {
        when(it){
            "wch" -> 1
            "cyz" -> 2
            "xxx" -> 3
            else -> -1
        }
    }
    println(list2)

flatMap

  1. flatMap用于对集合进行转换和扁平化操作。
  2. flatMap{it == 每个元素} 每个元素返回集合List<XXX>,最终结果 List<List<XXX>>
fun main() {
    val numbers = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6))

    // 使用flatMap将多个列表扁平化为一个列表
    val flattenedNumbers = numbers.flatMap { it }
    println(flattenedNumbers) // 输出: [1, 2, 3, 4, 5, 6]

    // 使用flatMap进行转换和扁平化操作
    val squaredNumbers = numbers.flatMap { it.map { number -> number * number } }
    println(squaredNumbers) // 输出: [1, 4, 9, 16, 25, 36]
}

filter

符合条件的添加到新集合中(true)

// 过滤掉int类型的list元素
    val list = listOf(listOf(1,2,3), listOf("a","b","c"), listOf(1.1, 2.2, 3.3))
    println(list) // [[1, 2, 3], [a, b, c], [1.1, 2.2, 3.3]]

    val list2 = list.filter {
        it.getOrNull(0) !is Int // 将Int集合全部去除
    }
    println(list2) // [[a, b, c], [1.1, 2.2, 3.3]]
// 全部扁平化展开后,去除Int
    val list3 = list.flatMap { sublist->
        sublist.filter { item->
            item !is Int
        }
    }
    println(list3) // [a, b, c, 1.1, 2.2, 3.3]

zip

合并集合

    // 以Pair形式合并元素
    val list = listOf(1,2,3)
    val list2 = listOf("a","b","c")
    var zipList = list.zip(list2)
    println(zipList)
    //输出结果:[(1, a), (2, b), (3, c)]

Kotlin和Java交互

可空性

Kotlin调用Java要注意为kong陷阱

// Java类
public class Person {
    String mName;
    public Person(){
    }
    public Person(String name){
        mName = name;
    }
}

错误交互:空指针

    // Kt和Java错误交互
    // name类型为 String! 可能为空
    val name = Person("wch").mName
    val name2 = Person().mName
    println(name.length)
    println(name2.length)

正确交互:规则一 小心使用

    val name = Person("wch").mName
    val name2 = Person().mName
    println(name?.length)
    println(name2?.length)

正确交互:规则二 强制判断

    val name:String? = Person("wch").mName
    val name2:String? = Person().mName
    println(name?.length)
    println(name2?.length)

@JvmName

改变Kotlin生成类的名称,方便Java调用,需要在kt文件最开头

@file:JvmName("Stu")

@JvmField

Java可以直接访问Kt字段,不再需要getXXX()

@JvmStatic

Java可以直接访问Kt Companion中静态字段和静态方法,而不需要MyKt.Companion.xxx

@JvmOverloads

Kt中默认参数,需要生成对应于Java的多个重载方法。

单例

饿汉式

object Singleton0

懒汉式(安全)

class Singleton private constructor(){
    companion object{
        // 底层采用双重检查加锁
        val instance:Singleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){Singleton()}
    }
}

mode属性:

  • LazyThreadSafetyMode.NONE: 这是默认的模式,懒加载属性是非线程安全的。
  • LazyThreadSafetyMode.SYNCHRONIZED: 懒加载属性是线程安全的。在多线程环境中,by lazy 会使用 synchronized 来确保只有一个线程可以初始化属性。这会导致多个线程在访问该属性时进行阻塞等待。
  • LazyThreadSafetyMode.PUBLICATION: 懒加载属性是线程安全的。在多线程环境中,by lazy 使用一种更高效的并发控制来进行初始化,可以在多个线程同时访问属性时进行初始化。这种模式通常比 SYNCHRONIZED 模式具有更好的性能。

LazyThreadSafetyMode.PUBLICATION不适合单例模式,可能创建更多的实例

函数和方法

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kotlin筑基 的相关文章

  • 如何使用分页库在回收器视图中添加日期分隔符?

    经过大量搜索 我知道使用常规适配器是可能的 但我不知道如何使用分页库来做到这一点 我不需要代码 只是一个线索 Example 要添加分隔符 您基本上有两个选择 基于视图 您显式地将分隔符作为 项目 包含在列表中 并为这些分隔符定义新的视图类
  • Spring Boot 2.1重复reactiveMongoTemplate bean

    我有以下 Spring Boot Configuration class Configuration EnableReactiveMongoRepositories class MongoConfiguration AbstractReac
  • 在 Android Kotlin 项目上使用 Java 11 库

    我有以下使用 Java 11 的 HttpClient 编写的 POST 请求 但 Android Studio 无法识别这些库 import java net URI import java net http HttpClient imp
  • Android Studio 中过时的 Kotlin 运行时警告

    下载并安装最新的 Kotlin 插件后 我有过时的 Kotlin 运行时来自 Android Studio 的警告告诉我 您在 kotlin stdlib 1 1 2 库中的 Kotlin 运行时版本是 1 1 2 而插件版本是1 1 2
  • 如何在 Kotlin Android 中正确使用 URL

    我想用 override fun onCreate savedInstanceState Bundle super onCreate savedInstanceState setContentView R layout activity m
  • Typealias - 在 Kotlin 中组合多个接口

    我对 Kotlin 中的协议组合有点生疏 我只想通过声明自定义来组合多个接口typealias This doesn t work typealias MyType ReadableInterface WritableInterface 有
  • 在 Android 中处理多个回收器视图 [Kotlin]

    我遇到过这样的情况 一个布局上有 3 个 RecyclerView 他们以某种方式相互依赖 数据来自房间数据库 问题原型 问题陈述 假设您有类似 Floor1 Floor2 Floor3 等 的楼层 并且每个楼层内都有类似 Room1 Ro
  • 如何在 IntelliJ 中快速文档中换行文本?

    Ctrl Q 快速文档 后我看不到所有文本 我必须水平滚动才能看到所有内容 我无法使编辑器适合窗口 更新 问题只是符号定义 即 第一行 描述 块文本 正确换行 实际上 我不想调整此窗口的大小 因为我更喜欢将其保留在相同的位置以便快速参考 我
  • groovy 无法解析 kotlin 类

    由于我无法解决我遇到的问题kotlin 我决定回到groovy为了实现该插件 但是我遇到了这个问题 我有这样的项目结构 为了不重写所有类 我决定重用中实现的类kotlin 然而我的课FileProcessingCoreGroovy Down
  • Ktor 如何处理空响应

    我使用 Ktor 和这样的线路myentity client get url 从 API 响应获取并反序列化我的实体 当 API 返回某些内容时它工作正常 但是一旦 API 没有任何可返回的内容并发送 HTTP 204 响应 它就会失败并显
  • 为什么 Kotlin 数据类对象有反引号?

    这是我使用 Kotlin 数据类创建器插件创建的数据类 data class ResponseHealthInisghts val data List
  • RecyclerView 适配器的 Kotlin 泛型

    我正在尝试编写一个通用的 recyclerview 适配器 我找到了几个例子 然而 仍然无法弄清楚如何实现通用适配器 我写的代码是 open abstract class BaseAdapter
  • Kotlin:覆盖子类型中的通用属性

    我试图编写一些通用代码 但无法摆脱Type of PROPERTY is not a subtype of the overridden property error 我的代码的简化版本 abstract class BaseP
  • LazyColumn - Items 关键参数的用途?

    有人可以解释一下它的主要目的是什么吗 key LazyListScope 的 items itemsIndexed 函数内的参数 如果我们指定该参数 我们会得到什么或不会得到什么 我不确定我是否理解与此参数相关的官方文档 key 代表该项目
  • Kotlin 中的单例类

    我想知道如何在 Kotlin 中创建一个单例类 以便我的 Util 类在每次应用程序执行时仅实例化一次 但是 当我将 Java 类转换为 kotlin 时 生成了以下代码 它是否正确 companion object private var
  • 在Android中绘制圆角矩形

    我已经发现这个问题 https stackoverflow com questions 5618402 how to draw rounded rectangle in android ui解决方案是这段代码
  • 用于 Kotlin 中单元测试的 BuildConfigField 模拟

    我正在尝试尽可能多地涵盖Kotlin Android 库我遇到了有关自定义 BuildConfig 变量的问题 更广为人知的是buildConfigField 我想模拟这个变量来测试两者true and false values 从 Gra
  • 如何使用 Google 的 GithubBrowserSample 方法在片段之间共享视图模型?

    我对 Android 架构组件的使用非常陌生 因此我决定使用 GithubBrowserSample 来构建我的应用程序来实现我的许多用例 但我有一个问题 我不知道使用这种方法在片段之间共享视图模型的正确方法是什么 我想共享视图模型 因为我
  • 为什么我们在 Kotlin 中使用“伴生对象”来替代 Java 静态字段?

    伴生对象 的本意是什么 到目前为止我一直用它来代替Java的static当我需要它的时候 我很困惑 为什么叫 同伴 呢 这是否意味着要创建多个static属性 我必须将其分组在里面companion object block 为了立即创建一
  • 如何在 IntelliJ Kotlin 项目中使用条件断点动态调试变量时更改值?

    在 Java 项目中 通过 mis 调试时可以实现这一点 使用条件断点为属性或变量设置值 Java断点 https i stack imgur com I4JL2 png 不幸的是 同样的事情在 Kotlin 项目中是不可能的 错误是 赋值

随机推荐

  • k8s学习pod第七天

    init Container 初始化容器是一类只运行一次的容器 本质是也是容器 不同容器间启动有先后顺序 只有前面的容器运行成功了 后面的容器才能运行 初始化容器的场景 在其他容器运行之前做个初始化 比如配置文件生成 环境变量生成 有先后顺
  • OpenCV——分水岭算法

    目录 一 分水岭算法 1 概述 2 图像分割概念 3 分水岭算法原理 二 主要函数 三 C 代码 四 结果展示 1 原始图像 2 分割结果 五 参考链接 一 分水岭算法 1 概述 分水岭算法是一种图像分割常用的算法 可以有效地将图像中的目标
  • Javascript高级程序设计——15-1.匿名函数和闭包

    1 匿名函数 表示没有定义函数名的函数 案例1 1 简单的匿名函数 function 单独的匿名函数无法执行 alert Lee 案例1 2 将匿名函数赋值给一个变量 var box function return Lee alert bo
  • 复数矩阵计算行列式

    项目上需要对复矩阵的行列式计算 根据计算一般矩阵行列式的代码改成了复矩阵行列式计算 include
  • 性能测试中TPS上不去的几种原因

    中TPS一直上不去 是什么原因 这篇文章 就具体说说在实际压力测试中 为什么有时候TPS上不去的原因 先来解释下什么叫TPS TPS Transaction Per Second 每秒事务数 指服务器在单位时间内 秒 可以处理的事务数量 一
  • Python库的使用说明

    目录 1 第三方库索引网站 2 第三方安装 2 1 pip工具介绍 2 2 pip工具安装 2 2 1 list 命令查看已安装的库列表 2 2 2 uninstall 命令 2 2 3 show 命令 2 2 4 download 命令
  • C++标准模板库 迭代器 iterator 详解(二)

    迭代器提供对一个容器中的对象的访问方法 并且定义了容器中对象的范围 迭代器就如同一个指针 事实上 C 的指针也是一种迭代器 但是 迭代器不仅仅是指针 因此你不能认为他们一定具有地址值 例如 一个数组索引 也可以认为是一种迭代器 迭代器有各种
  • [NOI2009]植物大战僵尸【拓扑+最大权闭合子图】

    题目链接 BZOJ 1565 看到这道题之后很容易想到的就是最大权闭合子图了 但是却有个问题就是要去除掉那些环 因为构成了环之后 相当于是无敌的状态 它们就永远不会得到贡献 并且环之后的点也是得不到贡献的 所以 这里利用拓扑 知道哪些点是可
  • 「Qt」事件概念

    0 引言 在本文所属专栏的前面的文章里 我们介绍了Qt的 信号 Signal 与 槽 Slot 机制 信号 Signal 与 槽 Slot 机制是 Qt 框架用于多个对象之间通信的 是 Qt 的核心特性 也是 Qt 与其他框架最大的不同之处
  • anaconda中spyder改变背景颜色(黑色)

    spyder挺好用的 但是未定义的背景颜色实在不好看 纯属个人审美 下面开始更换背景图 打开spyder 依此点击 Tools 再点击preference 喜爱 选择Syntax coloring Scheme调成Monokai 这是我喜欢
  • python+selenium+unittest自动化测试框架

    前言 关于自动化测试的介绍 网上已有很多资料 这里不再赘述 UI自动化测试是自动化测试的一种 也是测试金字塔最上面的一层 selenium是应用于web的自动化测试工具 支持多平台 多浏览器 多语言来实现自动化 优点如下 开源 免费且对we
  • pyecharts在数据可视化中的应用 (二)(pyecharts绘制树图、矩形树图、地理热力图、词云图、相关性矩阵等图)

    1 使用以下JSON数据绘制树图 矩形树图 from pyecharts import options as opts from pyecharts charts import Tree data name flare children n
  • Android 系统性能优化(57)---MTK 平台开关机、重启时间优化

    MTK 平台开关机 重启时间优化 开关机 重启时间优化 开机性能优化 是用功能和其它因素多方面平衡的结果 片面追求单方面的性能没有太大意义 有些产品设计开机动画非常酷炫 动画图片过多 高帧率会影响开机速度 这时就需要看是开机速度优先还是体验
  • 人工智能(pytorch)搭建模型8-利用pytorch搭建一个BiLSTM+CRF模型,实现简单的命名实体识别

    大家好 我是微学AI 今天给大家介绍一下人工智能 pytorch 搭建模型8 利用pytorch搭建一个BiLSTM CRF模型 实现简单的命名实体识别 BiLSTM CRF 模型是一种常用的序列标注算法 可用于词性标注 分词 命名实体识别
  • kubernetes资源控制器【一】- ReplicaSet控制器

    一 Pod控制器 Master的各组件中 API Server仅负责将资源存储于etcd中 并将其变动通知给各相关的客户端程序 如kubelet kube scheduler kube proxy和kube controller manag
  • id和instancetype的应用场景区别

    在 Objective C 中 id 是一个通用的指针类型 可以用来表示任何类型的对象 而instancetype是一个表示当前类类型的指针类型 通常用于方法的返回值类型 下面是它们的一些使用场景 使用id的情况 当你需要一个指向任何对象的
  • ubuntu 触摸板失灵解决

    ubuntu 触摸板失灵解决 Ubuntu 20 04 开机发现触摸板只能单击 经常漂移影响打字输入 操作 sudo modprobe r psmouse sudo modprobe psmouse 目的在于重新加载内核触摸板模块 重新加载
  • jquery ui 实现table的sortable功能以及过滤记录功能

    本人在工作中曾使用js实现过用鼠标拖动表格的行实现重新排序的功能 当时写了不少的js代码 最近发现jquery ui也能实现这个功能 而且很方便 真后悔当时不知道有这么个好东东 好 现在介绍下如何使用jquery ui来实现 引入的js文件
  • 邻结矩阵的创建

    图的邻结矩阵是储存图数据的一个手段 储存方式是用两个数组来表示圆 一个一维数组储存图中的顶点信息 一个二维数组 称为邻结矩阵 储存图中边或弧的信息 代码展示 include
  • Kotlin筑基

    Kotlin筑基 本文链接 核心思路 每个知识点都要和源码结合起来讲 文章目录 Kotlin筑基 编译时常量 基本类型 range 访问权修饰符 Unit Nothing 反引号 函数内联 函数引用 具名函数 判空和安全调用 断言操作 空合