kotlin - object关键字的使用

kotlin - object关键字的使用

kotlin中的object关键字主要有如下三种用途:

  • 对象表达式
  • 对象声明
  • 伴生对象

对象表达式

对象表达式是完全取代java的匿名内部类的,并且它可以指定多个父类型,和java的匿名内部类还是有区别的,java的匿名内部类只能实现一个父类型。
对象表达式的语法如下:

object:[0-N接口或类]{//后面接口和类不是必须的可以不写
    //对象表达式的类体部分
}

使用示例代码如下:

interface OnClickListener {
    fun onclick()
}

abstract class View {
    abstract fun draw()
}

fun main(args: Array<String>) {
    val view = object : View(), OnClickListener {
        override fun onclick() {
            println("点击了----")
        }

        override fun draw() {
            println("绘制")
        }
    }
    view.onclick()
    view.draw()
}

上面的程序反编译后java生成代码如下:

public final class ObjectDemoKt {
   public static final void main(@NotNull String[] args) {
      <undefinedtype> view = new OnClickListener() {
        ...
      };
      view.onclick();
      view.draw();
   }
}

从反编译后的生成的java代码可以看出,程序在遇到对象表达式的时候就会立即创建对象。
对象表达式有以下规则:

  • 对象表达式不能是抽象类,因为系统在创建对象表达式时会立即创建对象。
  • 对象表达式不能有构造方法。但可以有初始化块。
  • 对象表达式可以包含内部类(被inner修饰的类),但不能包含嵌套类。
    示例代码如下:
fun main(args: Array<String>) {
    var obj = object {//1
        //初始化块
        init {
            println("初始化块")
        }
        //属性
        var name = "kotlin"
        //方法
        fun test() {
            println("test方法")
        }
        inner class Empty
//        class Foo//编译器会报错
    }
    println(obj.name)
    obj.test()
}

上面的代码注释1处可以看出对象表达式没有指定父类型,这就是对象表达式的增强之处。对象表达式的本质其实就是增强版的匿名内部类。

kotlin中的对象表达式可以分为两种情况:

  • 在方法或者函数的局部范围内,或使用private修饰的对象表达式,kotlin编译器可以识别它真实的类型,比如上面的程序。
  • 非private修饰的对象表达式,编译器只会认为它是对父类或接口的处理。如果没有父类或接口系统认为它是Any类型。
    示例代码如下:
class ObjectTypeDemo{

    private val obj1 = object{
        val name:String="fefefe"
    }
    internal val obj2 = object {
        val name:String="fefefe"
    }
    private fun privateObj()=object {
        val name:String="fefefe"
    }
    fun publicObj()=object {
        val name:String="fefefe"
    }
    fun test(){
        //正确,可以识别它的真实类型
        println(obj1.name)
        //错误,不能识别它的真实类型
        println(obj2.name)
        //正确,可以识别它的真实类型
        println(privateObj().name)
        //错误,不能识别它的真实类型
        println(publicObj().name)
    }
}
fun main(args: Array<String>) {
  ObjectTypeDemo().test()
}

对象表达式和java的匿名内部类还有一点区别是,对象表达式可以访问和修改其他作用域的局部变量。而java中匿名内部类要访问其他作用域的变量,这个变量必须是final的。
示例程序如下:

fun main(args: Array<String>) {
    var a:Int=0
    val obj3 = object {
        fun chang(){
            a++
            println("a = $a")
        }
    }
    obj3.chang()
}

对象表达式比java的匿名内部类增强了以下三个方面:

  • 对象表达式可以指定多个父类型
  • kotlin编译器能更准确的识别局部范围内或private对象表达式的类型
  • 对象表达式可以访问和修改其所在范围内的局部变量。

对象声明

对象声明的语法格式如下:

object ObjectName:类型1,类型2,...,类型N{
    //对象声明的类体部分
}

示例代码如下:

object ObjectDeclaration{
    val name:String="declaration"
    fun test(){
        println("test方法")
    }
    class fefe{
        
    }
}

反编译后对象的java代码:

....
private ObjectDeclaration() {
}
static {
   ObjectDeclaration var0 = new ObjectDeclaration();
   INSTANCE = var0;
   name = "declaration";
}
.......

从上面的java代码可以看出kotlin的对象声明的本质就是在静态代码块中创建一个本类对象的实例。在使用的时候其实都是通过实例调用的,同时这也是一种单例模式,在类初始化的时候就会创建一个实例。在使用的时候可以通过类名.成员来访问

对象表达式与对象声明的区别:

  • 对象表达式可以赋值给变量,对象声明不可以。
  • 对象表达式可以包含内部类,不可以包含嵌套类,而对象声明可以包含嵌套类但不能包含内部类。
  • 对象表达式可以定义在方法或函数中,对象声明不可以。

伴生对象和静态成员

在类中定义的对象声明,可以用companion修饰,是它成为伴生对象。
kotlin中的伴生对象的主要目的就是解决kotlin取消了static关键字的问题。
示例代码如下:

class MyClass{
    companion object{
        val name :String="伴生对象"
        fun test(){
            println("test方法")
        }
    }
}
//使用
fun main(args: Array<String>) {
    println(MyClass.name)
    MyClass.test()
    println(MyClass.Companion)//1
}

由于我们是通过类名.成员的方式来访问伴生对象的成员的,所以伴生对象的名称也没什么意义,可以省略。如果我们要访问伴生对象可以通过注释1处的代码来访问。
伴生对象主要作用是为它所在的类模拟静态成员,但只是模拟,实际上伴生对象的成员还是属于伴生对象,并不属于它所在的外部类。
反编译以上代码所对应的java代码如下:

public final class MyClass {
   @NotNull
   private static final String name = "伴生对象";
   @NotNull
   public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);
    ...
   public static final class Companion {
      @NotNull
      public final String getName() {
         return MyClass.name;
      }

      public final void test() {
         String var1 = "test方法";
         boolean var2 = false;
         System.out.println(var1);
      }

      private Companion() {
      }
      ...
   }
}

从上面的java代码可以看出确实伴生对象的成员并不是静态的也不属于Myclass的成员。
要想把伴生对象的成员为其所在的外部类生成对象的静态成员,可以使用@JvmStatic这个注解来实现。代码如下:

class MyClass{
    companion object{
        @JvmStatic
        val name :String="伴生对象"
        @JvmStatic
        fun test(){
            println("test方法")
        }
    }
}

反编译以后java代码如下:

public final class MyClass {
   @NotNull
   private static final String name = "伴生对象";
   @NotNull
   public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);

   @NotNull
   public static final String getName() {
      MyClass.Companion var10000 = Companion;
      return name;
   }

   @JvmStatic
   public static final void test() {
      Companion.test();
   }
   ....
}

可以看出确实是在外部类下生成了对应的静态成员。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值