Kotlin 4 构造,对象,修饰符,关键字,委托

本文深入探讨了Kotlin中的构造函数、对象、修饰符、关键字等核心概念,并详细讲解了委托、延迟初始化、观察者模式及属性映射等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:当前文档为Kotlin自学总结,若什么地方含义模糊不清,敬请指教,谢谢:-)。

目录:

	- 构造函数
	- 对象
		- 匿名对象
		- 对象声明
	- 对象表达式和对象声明之间的语义差异
	- 修饰符
	- 关键字
	- 委托
		- 类委托
		- 属性委托
			- 要求
			- 延迟性
		- 观察者模式
		- 属性映射
		- 局部委托	
复制代码

构造函数 - 一个主构造多个次构造,主构造函数的所有的参数都有默认值

  • 主构造:(类声明之后)

      	//默认类声明 + 主构造
      	class ClazzName public constructor{
    
      	}
    
      	
      	class ClazzName public @Inject  constructor(val/var params1 : type = defultValue){
      			//constructor可省略,有注解时必须存在
      			//public 为可见性修饰符
      			init {
      				//init -> 初始化代码块,主构造的参数可以在初始化代码块使用
      			}
      	}
    复制代码
  • 次构造:委托或间接委托给主构造,需要 this 关键字(也可有constructor前缀)

      	class Person(val name: String) {//类声明 + 主构造
    
    
      	    constructor(name: String, parent: Person) : this(name) {
      			//次构造,相当于添加参数或重新赋值name
    
      	        //... ...
      	    }
    
    
      	}
    复制代码
  • 创建一般实例 - 没有new 关键字

      	val invoice = Invoice()
      	val customer = Customer("Joe Smith")		
      	//创建使用匿名对象,对象声明,单例请查看Kotlin-4.md
    复制代码

对象

一般对象的创建方式使用构造函数传参创建.

1. 匿名对象

一般匿名对象的使用

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ……
    }
})
复制代码

多个继承或实现并且需要传参的匿名对象

open class A(x: Int) {
    public open val y: Int = x
}

interface B {……}

val ab: A = object : A(1), B {
    override val y = 15
}
复制代码

没有继承或实现的匿名对象创建

fun foo() {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}
复制代码

公有函数返回值为匿名对象时,类型为Any,将不能访问匿名对象的成员.

class C {
    // 私有函数,所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有函数,所以其返回类型是 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // 没问题
        val x2 = publicFoo().x  // 错误:未能解析的引用“x”
    }
}
复制代码
对象声明

对象声明只能在其他对象声明或非内部类中.

//有继承的单例对象声明
object DataProviderManager : Parent() {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}

//直接调用
DataProviderManager.registerDataProvider(....)
复制代码

对象表达式和对象声明之间的语义差异

对象表达式和对象声明之间有一个重要的语义差别:

- 对象表达式是在使用他们的地方立即执行(及初始化)的
- 对象声明是在第一次被访问到时延迟初始化的
- 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配			
复制代码

修饰符

  • valvar

      	val 只读;var 可变.
      	(var默认创建getter,setter; val 默认创建 getter)
    复制代码
  • 可见性修饰符

    1. internal →代替→ default,意味着该成员只在相同模块内可见。更具体地说, 一个模块是编译在一起的一套 Kotlin 文件:

           一个 IntelliJ IDEA 模块;
           一个 Maven 或者 Gradle 项目;
           一次 <kotlinc> Ant 任务执行所编译的一套文件。
      复制代码
    2. 局部变量、函数和类不能有可见性修饰符。

关键字

  • this

    • 在类的成员中,this 指的是该类的当前对象.

    • 在扩展函数或者带接收者的函数字面值中, this 表示在"."左侧传递的接收者参数。

        	class A { // 隐式标签 @A
      
        	    inner class B { // 隐式标签 @B
      
        	        fun Int.foo() { // 隐式标签 @foo
        	            val a = this@A // A 的 this
        	            val b = this@B // B 的 this
        	
        	            val c = this // foo() 的接收者,一个 Int
        	            val c1 = this@foo // foo() 的接收者,一个 Int
        	
        	            val funLit = lambda@ fun String.() {
        	                val d = this // funLit 的接收者
        	            }
        	
        	
        	            val funLit2 = { s: String ->
        	                // foo() 的接收者,因为它包含的 lambda 表达式
        	                // 没有任何接收者
        	                val d1 = this
        	            }
      
        	        }
        	    }
        	}
      复制代码
  • abstract 抽象

  • companion → static(不存在此关键字) 伴生对象

    • 类没有静态方法。在大多数情况下,它建议简单地使用包级函数。

    • 需要访问其他类内部的函数,可以把它写成该类内对象声明中的一员。

        	class MyClass {
        	    companion object Factory { //Factory名称可以省略
        	        fun create(): MyClass = MyClass()
        	    }
        	}
      
      
        	//其他类中调用需要
        	val instance = MyClass.create()
        	
        	//名称省略后
        	val x = MyClass.Companion
      复制代码
  • const 编译时常量

      	请查看Kotlin-3.md 编译时常量内容
    复制代码
  • lateinit 延迟初始化属性使用

    • 使用字段可以避免编译时空检查

    • 只能与var配合使用

    • 属性类型不能为空或原生类型

    • 可以在单元测试的@setUp函数中初始化;

    • 初始化前访问属性会抛出"被访问且没有初始化"的异常.

        public class MyTest {
            lateinit var subject: TestSubject
        
            @SetUp fun setup() {
                subject = TestSubject()
            }
        
            @Test fun test() {
                subject.method()  // 直接解引用
            }
        }
      复制代码

委托

类委托

格式:

class A{
	fun aa()
	fun bb()
	...
}

class A1:A{
	override fun aa(){...}
	override fun bb(){...}
	...
}

class ClazzName(a:A):A by a{
	
	...
}

//使用
clazzName(A1()).aa()
复制代码

某个类通过关键字by,使用委托参数的方式、继承的格式,调用自身没有但该参数所在类标注的基类存在的所有公有函数的形式,叫做类委托.

原理:by 语句会将对应的委托对象进行内部存储,编译器将生成转发给该对象的所有标注基类的方法。

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b 
//Base为BaseImpl b的基类

fun main(args: Array<String>) {
    val b = BaseImpl(10)
    Derived(b).print() // 输出 10
}
复制代码
委托属性

使用**by关键字,将属性的getter、setter委托给函数或某个类的operator** 函数,

属性委托要求

对于一个只读属性(即 val 声明的),委托必须提供一个名为 getValue 的函数,该函数接受以下参数:

thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型, property —— 必须是类型 KProperty<*> 或其超类型, 这个函数必须返回与属性相同的类型(或其子类型)。

对于一个可变属性(即 var 声明的),委托必须额外提供一个名为 setValue 的函数,该函数接受以下参数:

thisRef —— 同 getValue(), property —— 同 getValue(), new value —— 必须和属性同类型或者是它的超类型。 getValue() 或/和 setValue() 函数可以通过委托类的成员函数提供或者由扩展函数提供。 当你需要委托属性到原本未提供的这些函数的对象时后者会更便利。 两函数都需要用 operator 关键字来进行标记。

委托类可以实现包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty 接口之一。 这俩接口是在 Kotlin 标准库中声明的:

interface ReadOnlyProperty<in R, out T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
}

interface ReadWriteProperty<in R, T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
    operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
复制代码
延迟性

延迟: (lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托.)第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果, 后续调用 get() 只是返回记录的结果

延迟是具有同步锁线程安全的,默认 LazyThreadSafetyMode.PUBLICATION传入lazy(),使用 LazyThreadSafetyMode.NONE 模式, 它不会有任何线程安全的保证和相关的开销。

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}
//输出内容
//computed!
//Hello
//Hello
复制代码
委托的观察者模式

Delegates.observable()Delegates.vetoable()

Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个 参数:被赋值的属性、旧值和新值;

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {//初始值
        prop, old, new -> //被赋值的属性、旧值和新值
        println("$old -> $new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "first"
    user.name = "second"
}

//打印内容为
//<no name> -> first
//first -> second
复制代码

Delegates.vetoable()可以截获一个赋值并“否决”它,在属性被赋新值生效之前会调用传递给 vetoable 的处理程序。

属性存储到映射 - 在一个映射(map)里存储属性的值。
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25
复制代码

val -> Map, var -> MutableMap

局部委托点击此处

本文参考自 Kotlin语言中心站

转载于:https://juejin.im/post/5a32177e6fb9a0450167f9f5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值