美文网首页Kotlin学习笔记
kotlin学习笔记:object关键字介绍与java中的静态变

kotlin学习笔记:object关键字介绍与java中的静态变

作者: 码途有道 | 来源:发表于2018-11-14 18:36 被阅读52次

    java中,静态变量静态方法是我们经常需要用到的东西,但是我们在kotlin中,并不能找到static关键字。其实目前在kotlin中,也的确是static概念的,那么我们该如何在kotlin中实现静态变量静态方法呢?这时就要用到kotlin中的object关键字了,下面文章的内容会将object的用法和静态的实现一起详细讲解

    Tip: 想要自己验证本文内容的小伙伴,请看文章《Kotlin学习笔记:如何将kotlin编译成java(必备小技能)》

    kotlin中的object

    从字面意思看,object的意思即是对象,实际上也确实如此,但是如此解释也未免过于抽象了!所以我们通过几个例子来看object

    首先,我们要知道object的几种使用场景:

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

    一、对象声明

    object Test{
        
    }
    

    一般声明一个类,我们用class,此处我们使用object来声明一个类,而在此同时也声明了它的一个对象。我们看一下,将kotlin转换成java后的代码:

    public final class Test {
       public static final Test INSTANCE;
    
       static {
          Test var0 = new Test();
          INSTANCE = var0;
       }
    }
    

    从转换成的java代码中,我们可以清楚的看到,在Test类中,同时声明了一个Test的静态变量对象INSTANCE,不仅如此,还形成了一个简单的单例模式,如果觉得这个不像,那么转换一下:

    public final class Test {
       private static final Test INSTANCE = new Test();
    
       public static Test getInstance(){
           return INSTANCE; 
       }
       // 必须注意的一点
       private Test(){
       
       }
    }
    

    看到这个,是否觉得有点眼熟了呢?
    不过有一点要注意,可能你们也发现了,kolin转换成的java代码中,没有将构造参数设为private,而我自己转换的却将构造参数设为了private,这是为什么呢?下面请看对Test类的调用

    在这里插入图片描述
    这下写的很明白了,直接提示private,所以其实第二段java代码才是kotlin转换后的完整版,至于为什么编译器转换出来的没有写明构造函数为private,就不得而知了。

    所以,总结object在以上的使用中,有两点

    • object声明一个类时,该类自动成为一个简单的单例模式
    • object声明的类,无法在外部用new的方式重新实例化

    代替static的第一种方法

    看了上面的object对象声明,下面就可以来说一下,第一种代替静态的方法啦!没错,就是使用object类

    下面是kotlin中的代码

    object Test {
        var code = 1
    
        fun getData(){
            
        }
    }
    

    编译成java代码

    public final class Test {
       private static int code;
       public static final Test INSTANCE;
    
       public final int getCode() {
          return code;
       }
    
       public final void setCode(int var1) {
          code = var1;
       }
    
       public final void getData() {
       }
    
       static {
          Test var0 = new Test();
          INSTANCE = var0;
          code = 1;
       }
    }
    

    可以看到,在转换成的java代码中,int型的code变量getCode()方法都变成静态的了,下面再来看看如何调用
    kotlin中调用

    private fun check() {
          val code = Test.code
          Test.getData()
    }
    

    java中调用

    private void check(){
        Test.INSTANCE.getCode();
        Test.INSTANCE.getData();
    }
    

    我们可以看到,在java中调用时,我们必须通过INSTANCE来进行,并且code的获取使用了get方法,其实这点在上面转换代码中就可以看到转换成的codeprivate的,并不是静态变量,并且自动生成了gettersetter方法。而对于getData()方法,其实也不是真正的静态方法,都是基于单例来实现的

    对于这点,有些人可能是接受不了的,并且觉得内部的java实现很糟糕,想要渴求真正的静态,那么该如何解决呢?这下就得我们的@JvmField@JvmStatic注解出场的时候了:smirk:

    @JvmField与@JvmStatic的出场

    我们先看代码,
    首先是kotlin代码:

    object Test {
        @JvmField
        var code = 1
    
        @JvmStatic
        fun getData(){
    
        }
    }
    

    然后是转换后的java代码:

    public final class Test {
       @JvmField
       public static int code;
       public static final Test INSTANCE;
    
       @JvmStatic
       public static final void getData() {
       }
    
       static {
          Test var0 = new Test();
          INSTANCE = var0;
          code = 1;
       }
    

    我们发现,code变成真正的静态变量,而getData()方法也变成了真正的静态方法,下面是一些注意点

    • @JvmField消除了变量的gettersetter方法
    • @JvmField修饰的变量不能是private属性的
    • @JvmStatic只能在object类或者伴生对象companion object中使用,而@JvmField没有这些限制
    • @JvmStatic一般用于修饰方法,使方法变成真正的静态方法;如果修饰变量不会消除变量的gettersetter方法,但会使gettersetter方法和变量都变成静态,看例子

    kotlin代码

    object Test {
        @JvmStatic
        var code = 1
    }
    
    

    转换后的java代码

    public final class Test {
       private static int code;
       public static final Test INSTANCE;
    
       /** @deprecated */
       // $FF: synthetic method
       @JvmStatic
       public static void code$annotations() {
       }
    
       public static final int getCode() {
          return code;
       }
    
       public static final void setCode(int var0) {
          code = var0;
       }
    
       static {
          Test var0 = new Test();
          INSTANCE = var0;
          code = 1;
       }
    }
    

    二、伴生对象

    kotlin中每个类都可以给自己构造一个伴生对象companion object,看代码

    class Test {
        // MyTest 是伴生对象的名字,可以不写,不写默认为 companion 
        companion object MyTest{
            var code = 1
    
            fun getData(){
    
            }
        }
    }
    

    转换后的java代码

    public final class Test {
       private static int code = 1;
       public static final Test.MyTest MyTest = new Test.MyTest((DefaultConstructorMarker)null);
    
       @Metadata(
          mv = {1, 1, 10},
          bv = {1, 0, 2},
          k = 1,
          d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0005\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\t\u001a\u00020\nR\u001a\u0010\u0003\u001a\u00020\u0004X\u0086\u000e¢\u0006\u000e\n\u0000\u001a\u0004\b\u0005\u0010\u0006\"\u0004\b\u0007\u0010\b¨\u0006\u000b"},
          d2 = {"Lcom/homeprint/module/mine/Test$MyTest;", "", "()V", "code", "", "getCode", "()I", "setCode", "(I)V", "getData", "", "production sources for module module_mine"}
       )
       public static final class MyTest {
          public final int getCode() {
             return Test.code;
          }
    
          public final void setCode(int var1) {
             Test.code = var1;
          }
    
          public final void getData() {
          }
    
          private MyTest() {
          }
    
          // $FF: synthetic method
          public MyTest(DefaultConstructorMarker $constructor_marker) {
             this();
          }
       }
    }
    

    可以看出,转换后的java代码中,生成了一个MyTest的静态类,通过这个MyTest来管理我们的codegetData(),但其实这也没有真正的实现我们想要的静态
    kotlin中的调用

    private fun check() {
          // 方式 1
          val code = Test.code
          Test.getData()
          // 方式 2
          val code1 =Test.MyTest.code
          Test.MyTest.getData()
    }
    

    java中的调用

    private void check(){
          Test.MyTest.getCode();
          Test.MyTest.getData();
    }
    

    可以看出,在kotlin中调用时,可以选择或者不写MyTest静态类,两种方式,但是在java中必须得写MyTest。那么如何实现我们想要的真正静态呢?和上述 对象声明 中使用一样的方法(@JvmField和@JvmStatic)
    kotlin的代码:

    class Test {
        companion object MyTest{
            @JvmField
            var code = 1
    
            @JvmStatic
            fun getData(){
    
            }
        }
    }
    

    转换后的java代码:

    public final class Test {
       @JvmField
       public static int code = 1;
       public static final Test.MyTest MyTest = new Test.MyTest((DefaultConstructorMarker)null);
    
       @JvmStatic
       public static final void getData() {
          MyTest.getData();
       }
    
       @Metadata(
          mv = {1, 1, 10},
          bv = {1, 0, 2},
          k = 1,
          d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0000\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\b\u0010\u0005\u001a\u00020\u0006H\u0007R\u0012\u0010\u0003\u001a\u00020\u00048\u0006@\u0006X\u0087\u000e¢\u0006\u0002\n\u0000¨\u0006\u0007"},
          d2 = {"Lcom/homeprint/module/mine/Test$MyTest;", "", "()V", "code", "", "getData", "", "production sources for module module_mine"}
       )
       public static final class MyTest {
          @JvmStatic
          public final void getData() {
          }
    
          private MyTest() {
          }
    
          // $FF: synthetic method
          public MyTest(DefaultConstructorMarker $constructor_marker) {
             this();
          }
       }
    }
    

    可以看到,我们想要的真正的静态有了。

    三、静态变量的另一种写法

    在上述我们讲解了如何利用object实现真正的静态,然而对于静态常量,我们还有另一种实现方式

     const var code = 1
    
    • const只能修饰常量val
    • const只能在object类中或者伴生对象companion object中使用
    • const的效果等于@JvmField,两者不能同时使用

    四、对象表达式

    object上面讲了两种场景,现在讲最后一种场景对象表达式,此段内容与静态无关。

    我们在java中经常遇到这样的场景:

    // 定义一个接口
    public interface OnTestCallback{
        void onTest();
    }
    
    // 然后这样实现接口
    myTest.setOnTestCallback(new OnTestCallback(){
         @Override 
         public void onTest(){
         
         }
    });
    

    我们可以看到java中直接声明了一个匿名内部类来实现了接口,在而在kotlin中我们是没有办法使用new的,那么怎么办呢?答案:使用对象表达式,看代码

    // 定义一个接口
    interface OnTestCallback{
        fun onTest();
    }
    
    // 然后这样实现接口
    myTest.setOnTestCallback(object:OnTestCallback{
         override fun onTest(){
         
         }
    });
    

    转换成java代码

    myTest.setOnTestCallback((OnTestCallback)(new OnTestCallback() {
         public void onTest() {
         
         }
    }));
    

    可以看出,在上述代码中,我们借助了object关键字来声明了接口对象。所以,object关键字此时的作用就是帮助我们声明匿名对象。

    总结

    关于object静态的讲解暂时就告一段落了,算是记录一下之前学习所得(作为一个强迫症,以前被它搞得很烦躁),防止以后遗忘!如果有不妥之处,欢迎指正!

    相关文章

      网友评论

        本文标题:kotlin学习笔记:object关键字介绍与java中的静态变

        本文链接:https://www.haomeiwen.com/subject/cktsfqtx.html