kotlin学习笔记:object关键字介绍与java中的静态变量和静态方法的实现以及@JvmField和@JvmStatic的使用

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类的调用

kotlin学习笔记:object关键字介绍与java中的静态变量和静态方法的实现以及@JvmField和@JvmStatic的使用_第1张图片
在这里插入图片描述

这下写的很明白了,直接提示 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关键字详解

你可能感兴趣的:(kotlin学习笔记:object关键字介绍与java中的静态变量和静态方法的实现以及@JvmField和@JvmStatic的使用)