在
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方法
,其实这点在上面转换代码中就可以看到转换成的code
是private
的,并不是静态变量,并且自动生成了getter
和setter
方法。而对于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
消除了变量的getter
与setter
方法 -
@JvmField
修饰的变量不能是private
属性的 -
@JvmStatic
只能在object类
或者伴生对象companion object
中使用,而@JvmField
没有这些限制 -
@JvmStatic
一般用于修饰方法,使方法变成真正的静态方法;如果修饰变量不会消除变量的getter
与setter
方法,但会使getter
与setter
方法和变量都变成静态,看例子
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
来管理我们的code
和getData()
,但其实这也没有真正的实现我们想要的静态
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
与静态
的讲解暂时就告一段落了,算是记录一下之前学习所得(作为一个强迫症,以前被它搞得很烦躁),防止以后遗忘!如果有不妥之处,欢迎指正!