Kotlin中的object 与companion object的区别

本文详细介绍了Kotlin中`object`关键字的两种用法,包括对象表达式和对象声明,并重点讨论了companion object的特性。通过示例解释了为何在companion object中不能直接调用外部非静态成员变量,原因是它们在Java中等同于静态方法,需要静态变量配合。文章最后给出了解决方案,即把引用的成员变量也声明为静态,以实现调用。

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

之前写了一篇Kotlin中常量和静态方法的文章,最近有人提出一个问题,在companion object中调用外部的成员变量会调用不到,这才意识到问题,本篇文章会带着这个疑问来解决问题。

一、 object关键字

object 关键字可以表达两种含义:一种是对象表达式,另一种是 对象声明

1、对象表达式

继承一个匿名对象

val textView = findViewById<TextView>(R.id.tv)
textView.setOnClickListener(object : OnClickListener {
        override fun onClick(p0: View?) {
            Toast.makeText(this@TestActivity, "点击事件生效", Toast.LENGTH_LONG)
        }

})

上面代码其实就是我们经常要给 view 设置的点击事件,OnClickListener 事件是一个匿名类的对象,用object来修饰。

2、对象声明

object 修饰的类为静态类,里面的方法和变量都为静态的。

2.1 直接声明类

object DemoManager {
    private val TAG = "DemoManager"
        
    fun a() {
        Log.e(TAG,"此时 object 表示 声明静态内部类")
    }
    
}

2.2 声明静态内部类

类内部的对象声明,没有被inner 修饰的内部类都是静态的

class DemoManager{
    object MyObject {
        fun a() {
            Log.e(TAG,"此时 object 表示 直接声明类")
        }
    }
}

如果需要调用 a()方法

kotlin中调用

fun init() {
    MyObject.a()
}

java中调用

 MyObject.INSTANCE.a();
 

二、companion object

companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。

companion object {
    private val TAG = "DemoManager"

    fun b() {
        Log.e(TAG,"此时 companion objec t表示 伴生对象")
    }
}

kotlin 中调用

fun init(){
       b()
}

java 中调用

DemoManager.Companion.b();

companion object 相关的内容可以查阅 Kotlin中常量和静态方法 这篇文章,在这里不多在具体描述。

三、在companion object中如何调用外部的成员变量

3.1 为什么companion object 中调用不到外部成员变量

class DemoManager {
    private val MY_TAG = "DemoManager"
   
    fun init(){
       b()
   }

    companion object {
        fun b() {
            Log.e(MY_TAG,"此时 companion objec t表示 伴生对象")
        }
    }
}

在上面代码中MY_TAG 是不会被调用到的。

原理很简单:

在java中我们写一个静态方法,如果需要调用成员变量,是无法调用到的

private String TAG = "MainActivity";
  
public static void init(){
        Log.e(TAG,"init() ");
}

只有将 TAG 修改为静态成员变量才能调用到

private static String TAG = "MainActivity";
  
public static void init(){
        Log.e(TAG,"init() ");
}

由此可以看出来,java中静态方法调用成员变量,要求成员变量必须是静态的, 在kotlin 中也是一样,所以当companion object 中调用非静态的成员变量也是调用不到的。

3.2 怎样解决才能调用到呢?

companion object {
    private val MY_TAG = "DemoManager"
    fun b() {
        Log.e(MY_TAG,"此时 companion objec t表示 伴生对象")
    }
}

将所引用的成员变量也修饰静态的,这样就可以引用到了。

最后,希望此篇博客对大家有所帮助,欢迎提出问题及建议共同探讨,如有兴趣可以关注,谢谢!

<think>好的,我现在要帮用户了解Kotlin中的companion object,也就是伴生对象。首先,我需要回忆一下相关的知识,并结合提供的引用资料来组织答案。 首先,根据引用3,companion objectobject关键字的三种使用场景之一,其他两种是对象声明和对象表达式。伴生对象主要用于在类中定义类本身关联而不是实例关联的成员,类似于Java中的静态成员。但Kotlin没有static关键字,所以用伴生对象来实现类似功能。 接下来,引用4给出了一个例子,companion object中可以包含用@JvmStatic注解的方法,这样在Java中调用时会更方便。这说明伴生对象的方法可以被编译成静态方法,方便Java互操作。 引用5提到伴生对象可以有名字,也可以没有名字,如果实现接口的话会提供默认实现。这意味着伴生对象可以更灵活地组织代码,甚至实现接口,增强扩展性。 然后,我需要定义伴生对象的基本概念:它是类内部的一个对象,类本身关联,而不是实例。每个类只能有一个伴生对象,类似于Java的静态成员,但功能更强大,因为伴生对象可以继承类和实现接口。 接下来是使用场景。根据引用3和5,伴生对象适合工厂方法、类级别常量、静态工具方法。比如,在类中定义一个创建实例的工厂方法,或者保存全局配置的常量。 然后需要提到如何定义伴生对象。语法是在类内部使用companion object关键字,可以带名字或者不带。例如: class MyClass { companion object Factory { fun create(): MyClass = MyClass() } } 或者不命名: companion object { ... } 还要提到@JvmStatic和@JvmField注解,用于生成真正的静态成员,方便Java调用。这是实际开发中常见的做法,特别是在混合项目中使用时需要注意。 另外,伴生对象可以实现接口,这样可以在需要接口实现的地方使用伴生对象,增加灵活性。比如: interface Factory<T> { fun create(): T } class MyClass { companion object : Factory<MyClass> { override fun create(): MyClass = MyClass() } } 这样,可以通过MyClass.Companion.create()来调用。 可能还需要对比伴生对象和Java静态成员的区别,强调Kotlin的伴生对象更灵活,可以继承和实现接口,而Java的static方法不能。 最后,根据用户的问题,生成相关问题,比如伴生对象和对象声明的区别,或者如何实现静态常量等。确保问题覆盖常见的疑惑点。 检查引用是否准确,引用3和5提到了伴生对象的命名和接口实现,引用4提到了@JvmStatic的使用,这些都需要在回答中正确引用。例如,在提到伴生对象可以有名字时引用5,提到@JvmStatic时引用4。 需要确保回答结构清晰,分点说明定义、使用场景、语法和注意事项,同时给出示例代码,让用户更容易理解。还要注意使用中文,数学公式用$...$,但这里可能不太涉及数学内容,所以主要关注代码块和引用。</think>### Kotlin伴生对象(companion object)详解 #### 一、核心定义 伴生对象是通过$companion\ object$关键字在类内部声明的特殊对象,类本身生命周期绑定,每个类只能有一个伴生对象[^3]。其本质是「类的内部单例对象」,用于替代Java的静态成员。 ```kotlin class Logger { companion object Factory { // 可命名伴生对象 fun create(): Logger = Logger() } } ``` #### 二、核心特性 1. **类级别访问**:通过类名直接访问成员 ```kotlin val logger = Logger.create() // 无需实例化Logger类 ``` 2. **命名自由**:支持匿名或显式命名(默认名称$Companion$)[^5] 3. **接口实现**:可继承接口实现多态 ```kotlin interface Factory<T> { fun produce(): T } class Car { companion object : Factory<Car> { override fun produce() = Car() } } ``` #### 三、典型使用场景 | 场景类型 | 示例代码 | 说明 | |-------------------|--------------------------------------------------------------------------|-----------------------------| | 工厂模式 | `Car.produce()` | 创建类实例的统一入口[^3] | | 静态常量 | ```companion object { const val MAX_SIZE = 1024 }``` | 编译时常量声明 | | 工具方法 | ```fun parse(json: String) = ...``` | 类相关但无需实例的方法 | | 接口适配 | ```companion object : JsonConverter<Data>``` | 实现JSON序列化等通用协议 | #### 四、JVM平台互操作 1. **@JvmStatic注解** 生成真正的静态方法(Java可调用): ```kotlin companion object { @JvmStatic fun check() { ... } } ``` 2. **@JvmField注解** 暴露常量到Java: ```kotlin companion object { @JvmField val VERSION = "1.0" } ``` #### 五、对象声明的区别 | | 伴生对象 | 对象声明(object) | |------------------|-----------------------------------|------------------------------| | 关联性 | 绑定到宿主类 | 独立单例对象 | | 访问方式 | 通过类名访问 | 通过对象名直接访问 | | 初始化时机 | 首次访问宿主类时初始化[^2] | 首次访问时初始化 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值