Ran*_*ku' 141 kotlin kotlin-companion
"伴侣对象"的意图是什么?到目前为止,我一直在使用它只是为了static
在需要时替换Java .
我很困惑:
companion object
块内组合它?:
companion object {
val singleton by lazy { ... }
}
Run Code Online (Sandbox Code Playgroud)
这似乎是一种单一的方式.有什么更好的方法?
hot*_*key 105
"伴侣对象"的意图是什么?为什么称它为"伴侣"?
首先,科特林不使用的Java的概念static
成员,因为科特林都有自己的概念,object
小号,描述了使用单状态连接特性和功能,以及Java static
类的一部分可以在单方面进行优雅的表示:这是一个单对象可以通过类的名称来调用.因此命名:它是一个类附带的对象.
它的名字曾经是class object
和default object
,但后来它被认为是重命名为companion object
哪个更清晰,也符合一贯的斯卡拉伴侣的对象.
除了命名之外,它比Java static
成员更强大:它可以扩展类和接口,您可以像其他对象一样引用和传递它.
这是否意味着要创建多个静态属性,我必须在
companion object
块内组合它?
是的,这是惯用的方式.或者你甚至可以通过它们的含义将它们分组在非伴侣对象中:
class MyClass {
object IO {
fun makeSomethingWithIO() { /* ... */ }
}
object Factory {
fun createSomething() { /* ... */ }
}
}
Run Code Online (Sandbox Code Playgroud)为了立即创建一个作用于类的单例实例,我经常写
/*...*/
一下,这似乎是一种单一的方法.有什么更好的方法?
这取决于您在每种特定情况下的需求.您的代码非常适合存储绑定到第一次调用它时初始化的类的状态.
如果您不需要将它与类连接,只需使用对象声明:
object Foo {
val something by lazy { ... }
}
Run Code Online (Sandbox Code Playgroud)
您还可以删除lazy { ... }
委托以使属性初始化第一类的用法,就像Java静态初始化程序一样
您可能还会找到初始化单例状态的有用方法.
D3x*_*ter 19
为什么称它为"伴侣"?
此对象是实例的伴随对象.IIRC在这里进行了长时间的讨论:即将到来的改变类对象 - 重新思考
这是否意味着要创建多个静态属性,我必须在伴随对象块中将它们组合在一起?
是.每个"静态"属性/方法都需要放在此随播广告中.
为了立即创建一个作用于类的单例实例,我经常写
您不会立即创建单例实例.它是在singleton
第一次访问时创建的.
这似乎是一种单一的方式.有什么更好的方法?
而是去object Singleton { }
定义单例类.请参阅:对象声明
您不必创建实例Singleton
,只需使用它即可Singleton.doWork()
请记住,Kotlin提供其他东西来组织您的代码.现在有了简单静态函数的替代方法,例如,您可以使用顶级函数.
Yog*_*ity 13
当具有相关功能的类/对象属于在一起时,它们就像彼此的同伴。在这种情况下,同伴是指合作伙伴或同事。
更干净的顶级命名空间
当某个独立函数仅打算与某个特定类一起使用时,我们不是将其定义为顶级函数,而是在该特定类中定义它。这可以防止顶级命名空间的污染,并有助于 IDE 提供更多相关的自动完成提示。
包装方便
当类/对象在彼此提供的功能方面彼此密切相关时,将它们保留在一起很方便。我们节省了将它们保存在不同文件中并跟踪它们之间关联的精力。
代码可读性
只需查看同伴关系,您就会知道这object
为外部类提供了辅助功能,并且可能无法在任何其他上下文中使用。因为如果它要与其他类一起使用,它将是一个单独的顶级class
或object
或函数。
companion object
问题:同伴class
让我们看一下伴生对象解决的问题类型。我们将举一个简单的现实世界例子。假设我们有一个类User
来代表应用程序中的用户:
data class User(val id: String, val name: String)
Run Code Online (Sandbox Code Playgroud)
以及用于从数据库添加或删除的interface
数据访问对象:UserDao
User
interface UserDao {
fun add(user: User)
fun remove(id: String)
}
Run Code Online (Sandbox Code Playgroud)
现在,由于 的功能User
和实现UserDao
在逻辑上彼此相关,我们可以决定将它们组合在一起:
data class User(val id: String, val name: String) {
class UserAccess : UserDao {
override fun add(user: User) { }
override fun remove(id: String) { }
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
fun main() {
val john = User("34", "John")
val userAccess = User.UserAccess()
userAccess.add(john)
}
Run Code Online (Sandbox Code Playgroud)
虽然这是一个很好的设置,但其中存在几个问题:
UserAccess
/删除User
.UserAccess
可以创建我们不想要的多个实例。我们只希望在整个应用程序中进行一次数据访问object
(单例) 。User
UserAccess
与其他类一起使用或扩展。因此,它并没有明确我们想要做什么的意图。userAccess.add()
似乎userAccess.addUser()
不太优雅。我们更喜欢类似的东西User.add()
。解决方案:companion object
在User
课堂上,我们只需将这两个单词替换class UserAccess
为另外两个单词companion object
即可!上面提到的所有问题一下子就解决了:
data class User(val id: String, val name: String) {
companion object : UserDao {
override fun add(user: User) { }
override fun remove(id: String) { }
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
fun main() {
val john = User("34", "John")
User.add(john)
}
Run Code Online (Sandbox Code Playgroud)
扩展接口和类的能力是将伴生对象与 Java 的静态功能区分开来的功能之一。此外,同伴是对象,我们可以将它们传递给函数并将它们分配给变量,就像 Kotlin 中的所有其他对象一样。我们可以将它们传递给接受这些接口和类的函数并利用多态性。
companion object
用于编译时const
当编译时常量与类密切相关时,可以在companion object
.
data class User(val id: String, val name: String) {
companion object {
const val DEFAULT_NAME = "Guest"
const val MIN_AGE = 16
}
}
Run Code Online (Sandbox Code Playgroud)
这就是您在问题中提到的分组类型。这样我们就可以防止顶级命名空间被不相关的常量污染。
companion object
和lazy { }
该lazy { }
构造对于获得单例来说不是必需的。Acompanion object
默认情况下是单例,object
仅初始化一次并且是线程安全的。当其对应的类被加载时它被初始化。lazy { }
当您想要推迟 成员的初始化companion object
或者当您有多个成员并且您希望仅在第一次使用时逐个初始化时,请使用:
data class User(val id: Long, val name: String) {
companion object {
val list by lazy {
print("Fetching user list...")
listOf("John", "Jane")
}
val settings by lazy {
print("Fetching settings...")
mapOf("Dark Theme" to "On", "Auto Backup" to "On")
}
}
}
Run Code Online (Sandbox Code Playgroud)
在此代码中,获取list
和settings
是昂贵的操作。因此,我们仅在实际需要并首次调用它们时才使用lazy { }
构造来初始化它们,而不是一次性全部初始化。
用法:
fun main() {
println(User.list) // Fetching user list...[John, Jane]
println(User.list) // [John, Jane]
println(User.settings) // Fetching settings...{Dark Theme=On, Auto Backup=On}
println(User.settings) // {Dark Theme=On, Auto Backup=On}
}
Run Code Online (Sandbox Code Playgroud)
获取语句仅在第一次使用时执行。
companion object
用于工厂功能伴随对象用于定义工厂函数,同时保留 constructor
private
. 例如,newInstance()
以下代码片段中的工厂函数通过自动生成来创建用户id
:
class User private constructor(val id: Long, val name: String) {
companion object {
private var currentId = 0L;
fun newInstance(name: String) = User(currentId++, name)
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
val john = User.newInstance("John")
Run Code Online (Sandbox Code Playgroud)
请注意如何 constructor
保留private
,但companion object
可以访问 constructor
。当您想要提供多种方法来创建对象且对象构造过程很复杂时,这非常有用。
在上面的代码中,id
保证了下一代的一致性,因为 acompanion object
是单例,只有一个对象会跟踪id
,不会有任何重复的id
s。
另请注意,伴生对象可以具有currentId
表示状态的属性(在本例中)。
companion object
扩大伴生对象不能被继承,但我们可以使用扩展函数来增强它们的功能:
fun User.Companion.isLoggedIn(id: String): Boolean { }
Run Code Online (Sandbox Code Playgroud)
如果您不指定,则默认的类名称companion object
是。Companion
用法:
if (User.isLoggedIn("34")) { allowContent() }
Run Code Online (Sandbox Code Playgroud)
这对于扩展第三方库类的伴随对象的功能很有用。相对于 Javastatic
成员的另一个优势。
companion object
有一定关系的成员
当函数/属性与类不密切相关而只是有些相关时,建议您使用顶级函数/属性而不是companion object
. 最好在与类相同的文件中的类声明之前定义这些函数:
fun getAllUsers() { }
fun getProfileFor(userId: String) { }
data class User(val id: String, val name: String)
Run Code Online (Sandbox Code Playgroud)
保持单一责任原则
当功能很object
复杂或者类很大时,您可能希望将它们分成单独的类。例如,您可能需要一个单独的类来表示一个User
和另一个UserDao
用于数据库操作的类。UserCredentials
与登录相关的功能的单独类。当您有一个在不同地方使用的大量常量列表时,您可能希望将它们分组到另一个单独的类或文件中UserConstants
。UserSettings
代表设置的不同类。另一个类UserFactory
用于创建 的不同实例,User
依此类推。
就是这样!希望这有助于使您的代码更符合 Kotlin 语言。
为什么称其为“同伴”?
类内的对象声明可以用伴随关键字标记:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Run Code Online (Sandbox Code Playgroud)
可以通过仅使用类名作为限定符来调用伴随对象的成员:
val instance = MyClass.create()
Run Code Online (Sandbox Code Playgroud)
如果仅使用“对象”而不使用“同伴”,则必须这样做:
val instance = MyClass.Factory.create()
Run Code Online (Sandbox Code Playgroud)
以我的理解,“同伴”是指该对象与outter类相伴。
归档时间: |
|
查看次数: |
31449 次 |
最近记录: |