小网站关键词,重庆沙坪坝区,如何在aws上创建wordpress,哪些网站做推广效果好如果需要创建一个稍微修改了某个类的对象#xff0c;而不需要显式地声明一个新的子类。Kotlin可以通过对象表达式#xff08;object expressions#xff09;和对象声明#xff08;object declarations#xff09;来处理这种情况。
对象表达式
对象表达式用于创建匿名类的…如果需要创建一个稍微修改了某个类的对象而不需要显式地声明一个新的子类。Kotlin可以通过对象表达式object expressions和对象声明object declarations来处理这种情况。
对象表达式
对象表达式用于创建匿名类的对象。这种类对于一次性使用非常有用。你可以从头开始定义它们继承现有的类或者实现接口。匿名类的实例也被称为匿名对象因为它们是由表达式定义的而不是由名称定义的。
从头开始创建匿名对象
对象表达式以关键字object开始。
定义一个普通的无父类的匿名对象
val helloWorld object {val hello Helloval world World// 重写Any的toString方法override fun toString() $hello $world
}fun main() {print(helloWorld)
}继承
继承MouseAdapter类
window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { /*...*/ }override fun mouseEntered(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
}将匿名对象用作返回类型和值类型
当匿名对象用作本地或私有函数或属性的类型时非内联声明通过该函数或属性可以访问其所有成员
class C {private fun getObject() object {val x: String x}fun printX() {println(getObject().x)}
}如果获取匿名类的函数或属性是公共的或私有内联的则其实际类型为
如果匿名对象没有声明的超类型则为 Any如果有确切的一个声明的超类型则为匿名对象的声明的超类型如果有多于一个声明的超类型则为显式声明的类型
在所有这些情况下无法访问在匿名对象中添加的成员。如果在函数或属性的实际类型中声明了重写的成员则可以访问这些成员
interface A {fun funFromA() {}
}
interface Bclass C {// 返回类型是Anyx变量 不能访问fun getObject() object {val x: String x}// 返回类型是Ax变量 不能访问fun getObjectA() object: A {override fun funFromA() {}val x: String x}// 返回类型是BfunFromA()和 x变量 均不能访问fun getObjectB(): B object: A, B { // 需要显式返回类型override fun funFromA() {}val x: String x}
}访问匿名对象中的变量
对象表达式中的代码可以访问来自封闭范围的变量
fun countClicks(window: JComponent) {var clickCount 0var enterCount 0window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) {clickCount}override fun mouseEntered(e: MouseEvent) {enterCount}})
}对象声明
声明单例模式
object DataProviderManager {fun registerDataProvider(provider: DataProvider) {// ...}val allDataProviders: CollectionDataProvider MutableList(0){DataProvider()}get() field// ...
}对象声明是在 object 关键字后面跟着一个名称。就像变量声明一样对象声明不是一个表达式不能在赋值语句的右侧使用
对象声明在首次访问时进行初始化是线程安全的
要引用对象直接使用它的名称
DataProviderManager.registerDataProvider(...)继承
object DefaultListener : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { ... }override fun mouseEntered(e: MouseEvent) { ... }
}对象声明不能在方法内部不能是局部对象 数据对象
打印一个普通的对象声明时其字符串表示包含了它的名称和对象的哈希值
object MyObjectfun main() {println(MyObject()) // MyObject1f32e575
}就像数据类一样你可以用 data修饰符标记一个对象声明。这会告诉编译器为你的对象生成一些函数
toString() 返回数据对象的名称equals() 和 hashCode() 成对出现
对于数据对象不能提供自定义的 equals 或 hashCode 实现
数据对象的 toString() 函数返回对象的名称
data object MyDataObject {val x: Int 3
}fun main() {println(MyDataObject) // MyDataObject
}equals() 函数用于数据对象确保具有与你的数据对象相同类型的所有对象都被视为相等。在大多数情况下你在运行时只会有一个数据对象的实例毕竟数据对象声明了一个单例。然而在特殊情况下如果在运行时生成了另一个相同类型的对象例如通过使用平台反射与java.lang.reflect或使用此 API 底层的 JVM 序列化库这确保这些对象被视为相等 确保你只通过结构比较数据对象使用 操作符而绝不是通过引用比较使用 操作符。这有助于在运行时存在多个数据对象实例时避免陷阱 data object MySingletonfun main() {val evilTwin createInstanceViaReflection()println(MySingleton) // MySingletonprintln(evilTwin) // MySingleton// 使用 比较两个数据类返回 trueprintln(MySingleton evilTwin) // true// 不要通过 比较两个数据类println(MySingleton evilTwin) // false
}fun createInstanceViaReflection(): MySingleton {// kotlin反射不允许创建数据对象// 当前方法会强制创建一个MySingleton对象// 不要在代码中这样做return (MySingleton.javaClass.declaredConstructors[0].apply { isAccessible true } as ConstructorMySingleton).newInstance()
}生成的 hashCode() 函数与 equals() 一样 因此数据对象的所有运行时实例都具有相同的哈希码
数据对象和数据类之间的差异 虽然数据对象和数据类声明通常一起使用并具有一些相似之处但有一些函数不会为数据对象生成
没有copy()函数。因为数据对象声明旨在用作单例对象所以不会生成 copy()函数。单例模式将类的实例化限制为单个实例允许创建实例的副本将违反这一原则没有 componentN() 函数。与数据类不同数据对象没有任何数据属性。由于在没有数据属性的情况下尝试解构这样的对象是没有意义的因此不会生成 componentN() 函数
使用数据对象处理密封层次结构
数据对象声明在处理密封层次结构如密封类或密封接口时特别有用这可以更简洁的定义一个与其他密封类相同的类型
sealed interface ReadResult
data class Number(val number: Int) : ReadResult
data class Text(val text: String) : ReadResult
data object EndOfFile : ReadResultfun printReadResult(r: ReadResult) {when(r) {is Number - println(Num(${r.number})is Text - println(Txt(${r.text})is EndOfFile - println(EOF)}
}fun main() {printReadResult(EndOfFile) // EOF
}伴生对象
在类内部可以使用 companion关键字标记的对象声明
class MyClass {companion object Factory {fun create(): MyClass MyClass()}
}伴生对象的成员可以通过使用类名作为限定符而被简单调用
val instance MyClass.create()伴生对象的名称可以省略默认名称Companion
class MyClass {companion object{ }
}val x MyClass.Companion
fun main() {println(x) // io.example.MyClass$default35fb3008
}类成员可以访问相应伴生对象的私有成员
一个类的名称单独使用而不是作为另一个名称的限定符充当对该类的伴生对象的引用无论是否命名
class MyClass {companion object{ }
}val y MyClass // 外部类名称做伴生对象的引用
fun main() {println(y) // io.example.MyClass$default35fb3008
}请注意尽管伴生对象的成员看起来像是其他语言中的静态成员但在运行时它们仍然是真实对象的实例成员因此可以实现接口
interface FactoryT {fun create(): T
}class MyClass {companion object : FactoryMyClass {override fun create(): MyClass MyClass()}
}fun main() {val f: FactoryMyClass MyClassprintln(f.create()) // io.example.MyClass7225790e
}在 JVM上如果使用 JvmStatic 注解可以将伴生对象的成员生成为真正的静态方法和字段。有关详细信息查看这里。
对象表达式和对象声明之间存在一个重要的语义差异
对象表达式是立即执行和初始化的它们在使用的地方立即执行。对象声明是懒初始化的当首次访问时才会初始化。伴生对象在相应的类被加载解析时初始化这符合 Java静态初始化程序的语义