kotlin部分常用特性总结

发布于:2025-07-24 ⋅ 阅读:(14) ⋅ 点赞:(0)

Kotlin中类和对象初始化

  • 添加open关键字代表可以被继承
  • Any 是所有类的父类,类似Object,包含 equals() hashCode() toString()方法
  • constructor 关键字代表构造函数, constructor关键字可以去掉
  • 辅助构造函数是类中定义的额外构造函数,用于提供多种对象初始化方式
  • 主构造函数可以在类头部分声明,以及通过 init 块或属性直接赋值
class User(性格: String, 长相: String, 声音: String) : Human(性格, 长相, 声音)
class User2(name: String) : Human2(name)
open class Human constructor(var 性格: String, var 长相: String, var 声音: String) : Any() {
    //对象创建时调用,构造方法的方法体
    init {
        println("new 了一个${this.javaClass.simpleName}, ta性格:$性格, 长相:$长相, 声音:$声音")
    }
    override fun toString(): String {
        return "${性格} 性格"
    }
}
open class Human2 {
    val name: String
    val age: Int
    constructor(name: String) : this(name, 0) {
        // 调用主构造函数或另一个辅助构造函数
    }

    constructor(name: String, age: Int = 18) {  // 辅助构造函数
        this.name = name
        this.age = age
        val user: User = User("温柔", "甜美", "动人")

        val value: String? = getName()
        println(value!!.length)
    }
}

空类型和智能类型转换

  • ?.在遇到null时会静默跳过执行(返回null)
  • !!在遇到null时会立即抛出异常 (当对可空变量使用!!时,编译器会假定该变量不为null)
fun getName(): String? {// ? 表示返回值可以为空
    return "na"//null
}
fun main(args: Array<String>) {
    val str: String? = null
    // ?.在遇到null时会静默跳过执行(返回null)
    // !!在遇到null时会立即抛出异常 (当对可空变量使用!!时,编译器会假定该变量不为null)
    //val length = str!!.length
    // 应尽量避免使用!!,推荐替代方案包括:
    // 优先使用val声明不可变变量
    // 对延迟初始化变量使用 lateinit. var用lateinit 延迟初始化,val用lazy
    lateinit var q: String
    // 使用Elvis操作符?:提供默认值或者return
    //Elvis操作符?: 结构:表达式A ?: 表达式B
    //当表达式A非null时返回A的值,否则返回表达式B的结果
    // 例如:
    val length2 = str?.length ?: 0
    val name: String = getName() ?: return
    println(name.length)
    main2(args)
}

区间 Range,表示范围

//val:用于运行时常量,相当于Java的final字段,支持任何类型且仅初始化一次
//const:用于编译时常量,相当于Java的public static final字段,可通过类名直接访问。
//编译时确定值:const修饰的常量值必须在编译时确定,不能通过运行时计算或动态赋值。
//类型限制:仅支持基本类型(如int、double、string)和字符串。
//存储位置:必须声明在顶层(如包级可见变量)或伴生对象(companion object)中
val range: IntRange = 0..1024 // [0, 1024]
val range_exclusive: IntRange = 0 until 1024 // [0, 1024) = [0, 1023]
val a = (50 in range) // (i in range)判断i是否在区间中

Unit

在 Kotlin 中,Unit 是一个特殊的类型,类似于 Java 中的 void,但有以下关键区别:

  • Unit 是一个实际存在的类型(单例对象),而 void 只是关键字
  • 当函数不返回有意义的值时,Kotlin 会隐式返回 Unit
  • Unit 的实例可以用 Unit 或 () 表示
fun printMessage(msg: String): Unit { // 这里的 : Unit 可以省略
    println(msg)
    // 隐式返回 Unit
}
fun printUsage() {
    println("请传入两个整型参数,例如 1 2") // (Any?) -> Unit
} // ()->Unit

val sum = { arg1: Int, arg2: Int ->
    println("$arg1 + $arg2 = ${arg1 + arg2}")
    arg1 + arg2
}
// (Int, Int) -> Int
val printlnHello = {
    println(::printUsage is ()-> Unit)
    println("Hello")
}
// ()-> Unit
val int2Long = fun(x: Int): Long {
    return x.toLong()
}

继承与实现

  • 父类需要 open 才可以被继承
  • 父类方法、属性需要 open 才可以被覆写
  • 接口、接口方法、抽象类默认为 open
  • 覆写父类(接口)成员需要 override 关键字
  • class D: A(),B,C
  • 注意继承类时实际上调用了父类构造方法
  • 类只能单继承,接口可以多实现

继承之接口代理(基于 by 关键字)

by关键字在Kotlin中用于**委托模式**,它允许你将一个类的某个属性或方法的实现委托给另一个对象。- 它可以让我们在不直接实现接口的情况下,通过委托的方式间接实现接口 通过 by 将接口实现委托给其他对象,无需手动编写代理类
class SeniorManager(val driver: Driver, val writer: Writer): Driver by driver, Writer by writer

class CarDriver: Driver {
    override fun drive() {
        println("开车呢")
    }
}

class PPTWriter: Writer {
    override fun write() {
        println("做PPT呢")
    }
}

interface Driver{
    fun drive()
}
interface Writer{
    fun write()
}

fun main3(args: Array<String>) {
    val driver = CarDriver()
    val writer = PPTWriter()
    val seniorManager = SeniorManager(driver, writer)
    seniorManager.drive()
    seniorManager.write()
}

接口方法冲突

  • 接口方法可以有默认实现
  • 签名一致且返回值相同的冲突
  • 子类(实现类)必须覆写冲突方法
  • super<[父类(接口)名]>.方法名
abstract class A{
    open fun x(): Int = 5
}
interface B{
    fun x(): Int = 1
}
interface C{
    fun x(): Int = 0
}
class D(var y: Int = 0): A(), B, C{
    override fun x(): Int {
        println("call x(): Int in D")
        if(y > 0){
            return y
        }else if(y < -200){
            return super<C>.x()
        }else if(y < -100){
            return super<B>.x()
        }else{
            return super<A>.x()
        }
    }
}
//3 5 1 0 
fun main(args: Array<String>) {
    println(D(3).x())
    println(D(-10).x())
    println(D(-110).x())
    println(D(-10000).x())
}

可见性对比

模块通常指一组共同编译的 Kotlin 文件(如 Gradle 模块或 IntelliJ 模块)

kotlin java
private private
protected protected
- default(包内可见)
internal(模块内可见,模块化开发的核心修饰符) -
public public

Object 关键字

object 关键字用于实现‌单例模式‌、‌伴生对象‌和‌对象表达式‌(匿名对象),是 Kotlin 特有的简化设计模式的语法糖。以下是其三大核心用途及示例:

  1. 单例模式(对象声明)‌
    直接通过 object 声明线程安全的单例:
object DatabaseManager {
    private val connection = "DB_Connection"
    fun query(sql: String) = println("Executing: $sql via $connection")
}
// 使用(全局唯一实例)
fun main() {
    DatabaseManager.query("SELECT * FROM users")  // 输出: Executing: SELECT * FROM users via DB_Connection
}

特点:
* 只有一个实例的类
* 首次访问时延迟初始化(线程安全)
* 不能自定义构造函数, 可继承父类 可实现接口

  1. 伴生对象
  • 每个类可以对应一个伴生对象
  • 伴生对象的成员全局独一份 , 相当于静态方法 静态属性
class Latitude private constructor(val value: Double){
    companion object{
        @JvmStatic
        fun ofDouble(double: Double): Latitude{
            return Latitude(double)
        }

        fun ofLatitude(latitude: Latitude): Latitude{
            return Latitude(latitude.value)
        }

        @JvmField
        val TAG: String = "Latitude"
    }
}
  1. 匿名对象
  • 每次调用都会创建新对象
  • 可同时继承类和实现接口(如 object : ClassA(), InterfaceB
  • 注意:object 与 class 不同,不能实例化(本身就是实例)
    interface ClickListener {
        fun onClick()
    }

    fun setClickListener(listener: ClickListener) {
        listener.onClick()
    }

    fun main() {
        var counter = 0
        setClickListener(object : ClickListener {
            override fun onClick() {
                println("Clicked ${++counter} times")  // 捕获并修改外部变量
            }
        })
    }

扩展成员

  • 为现有类添加方法、属性
  • fun X.y(): Z {... }
  • val x.m 注意扩展属性不能初始化,类似接口属性
  • Java 调用扩展成员类似调用静态方法
    operator fun String.times(int: Int): String{
        val stringBuilder = StringBuilder()
        for(i in 0 until int){
            stringBuilder.append(this)
        }
        return stringBuilder.toString()
    }

    val String.a: String
        get() = "abc"

    var String.b: Int
        set(value) {
        }
        get() = 5
    //abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
    //5
    fun main(args: Array<String>) {
        println("abc" * 16)
        "abc".b = 5
        println("abc".b)
    }

operator关键字

operator 关键字用于‌重载运算符‌或‌约定特定函数‌,让类实例支持类似基础类型的操作(如 +、[]、in 等)

运算符重载‌

通过重载预定义的运算符函数,使对象支持算术、比较等操作

data class Point(val x: Int, val y: Int) {
    // 重载 "+" 运算符
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
}
fun main() {
    val p1 = Point(1, 2)
    val p2 = Point(3, 4)
    println(p1 + p2)  // 输出: Point(x=4, y=6)
}
运算符 对应函数名 示例表达式
+ plus a + b
- minus a - b
[] get a[i]
in contains a in b
== equals a==b
约定函数

operator 还可用于标记‌特定名称的函数‌,实现与语言特性的交互:

  • 解构声明(Destructuring)
class User(val name: String, val age: Int) {
    operator fun component1() = name  // 解构为 (name, age)
    operator fun component2() = age
}
fun main() {
    val user = User("Alice", 25)
    val (name, age) = user  // 解构赋值
    println("$name, $age")  // 输出: Alice, 25
}

  • 迭代器支持
class Counter(val range: IntRange) {
    operator fun iterator(): Iterator<Int> = range.iterator()
}

fun main() {
    for (i in Counter(1..5)) {  // 支持 for-in 循环
        print("$i ")  // 输出: 1 2 3 4 5
    }
}

注意事项‌
  • 不可随意创造运算符‌:只能重载 Kotlin 预定义的运算符47。
  • 一致性要求‌:如 plus 不应修改原对象,应返回新实例35。
  • 与扩展函数结合‌:可为现有类添加运算符支持
operator fun Int.times(str: String) = str.repeat(this)
fun main() {
    println(3 * "Hi ")  // 输出: Hi Hi Hi 
}

属性代理

懒加载 lazy
  • 延迟到首次访问时执行
  • 线程安全模式‌:默认 LazyThreadSafetyMode.SYNCHRONIZED,可选 PUBLICATION(允许多线程初始化)或 NONE(单线程)
class Delegates{
    val hello by lazy {
        "HelloWorld"
    }
    val hello0 by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
        "HelloWorld"
    }

    val hello2 by X()

    var hello3 by X()
}
基本用法
// val 属性‌:只需实现 getValue 
‌// var 属性‌:需同时实现 getValue 和 setValue
class X{
    private var value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue: $thisRef -> ${property.name}")
        return value?: ""
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
        println("setValue, $thisRef -> ${property.name} = $value")
        this.value = value
    }
}

fun main6(args: Array<String>) {
    val delegates = Delegates()
    println(delegates.hello)
    println(delegates.hello2)
    println(delegates.hello3)
    delegates.hello3 = "value of hello3"
    println(delegates.hello3)
    println(delegates.hello)
}
//HelloWorld
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello2
//
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//
//setValue, com.spro.globalsearch.Delegates@1593948d -> hello3 = value of hello3
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//value of hello3
//HelloWorld

数据类DataClass

  • 自动生成标准方法: 数据类会自动生成 equals()、hashCode()、toString()、copy() 等方法,普通类需手动实现
  • 主构造函数要求: 必须至少有一个参数,且参数需标记为 val 或 va, 普通类无此限制
  • 解构声明支持 数据类自动支持解构,可直接将属性拆分为变,普通类则需手动实现 componentN()
  • 不可变性推荐‌: 属性通常声明为 val(不可变),但支持 var(可变)‌
  • 继承限制‌ 数据类不能是 abstract、open、sealed 或 inner 类‌
  • 无无参构造函数 需显式定义默认值或辅助构造函数‌
应用
  • 数据传输对象 如 JSON 解析后的数据实体‌
  • 简化数据操作 利用 copy() 实现不可变对象的修改‌ val user2 = user1.copy(age = 26)
data class Country(val id: Int, val name: String)
data class Country2(var id: Int, var name: String)
//component 组件
class ComponentX{
    operator fun component1(): String{
        return "您好,我是"
    }

    operator fun component2(): Int{
        return 1
    }

    operator fun component3(): Int{
        return 1
    }

    operator fun component4(): Int{
        return 0
    }
}

fun main7(args: Array<String>) {
    //componentN解构声明
    val china = Country(0, "中国")
    println(china)// Country(id=0, name=中国)
    println("${china.component1()}  ------ ${china.component2()} ")//0 ------ 中国
    val (idx, name) = china //解构赋值
    println("$idx ------  $name")//0 ----- 中国

    val componentX = ComponentX()//解构赋值
    val (a, b, c, d) = componentX
    println("$a $b$c$d")//您好,我是 110
}

内部类

静态内部类与非静态内部类的区别:到底是否持有外部类的状态 (非静态内部类持有外部类的状态,可以访问外部类的属性)

open class Outter{
    val a: Int = 0
    class InnerDefaultStatic{
    }
    //非静态内部类
    inner class Inner{
        val a: Int = 5
        fun hello(){
            println(this@Outter.a)
        }
    }
}
interface OnClickListener{
    fun onClick()
}
class View{
    var onClickListener: OnClickListener? = null
}
fun main(args: Array<String>) {
    val innerDefaultStatic = Outter.InnerDefaultStatic()
    val inner = Outter().Inner()
    val view = View()
    //匿名内部类看上去没有名字,但是编译生成字节码文件有自己的ID,类似Outter$1.class
    view.onClickListener = object : Outter(), OnClickListener{
        override fun onClick() {

        }
    }
}

枚举类

  • 实例可数的类,注意枚举也是类
  • 可以修改构造,添加成员
  • 可以提升代码的表现力,也有一定的性能开销
  • 实例可数‌指枚举(enum)类型的成员数量固定且不可扩展。
  • 枚举通过enum class声明,其所有成员(如VERBOSE、DEBUG等)均为该类的实例对象,且默认构造函数为私有(private constructor()),因此无法被继承或扩展
enum class LogLevel(val id: Int){
    VERBOSE(0), DEBUG(1), INFO(2), WARN(3), ERROR(4), ASSERT(5);

    fun getTag(): String{
        return "$id, $name"
    }

    override fun toString(): String {
        return "$name, $ordinal"
    }
}
    println(LogLevel.DEBUG.ordinal)
    LogLevel.values().map(::println)
    println(LogLevel.valueOf("ERROR"))
    //VERBOSE, 0
    //DEBUG, 1
    //INFO, 2
    //WARN, 3
    //ERROR, 4
    //ASSERT, 5
    //ERROR, 4

密封类

子类可数

sealed class PlayerCmd{
}
class Play(val url:String,val position:Long =0):PlayerCmd()
class Seek(val position: Long): PlayerCmd()
object Pause:PlayerCmd()
object Resume:PlayerCmd()
object Stop:PlayerCmd()

enum class PlayerState{
    IDLE, PAUSE, PLAYING
}


网站公告

今日签到

点亮在社区的每一天
去签到