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

在这里插入图片描述

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容