在
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
註解出場的時候了😏
@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
與靜態
的講解暫時就告一段落了,算是記錄一下之前學習所得(作爲一個強迫症,以前被它搞得很煩躁),防止以後遺忘!如果有不妥之處,歡迎指正!