Kotlin中遇到的問(wèn)題

介紹

這里記錄一下使用Kotlin中遇到的一些問(wèn)題。

Lambda問(wèn)題

我們?cè)陂_發(fā)App的時(shí)候經(jīng)常會(huì)使用觀察者模式訂閱某個(gè)主題,當(dāng)主題發(fā)生變化的時(shí)候通知所有觀察者,但是在Java中定義觀察者的邏輯后,在Kotlin中調(diào)用Java的時(shí)候就會(huì)出現(xiàn)奇怪的問(wèn)題。比如:

public class CurrentLocationProvider {
    private CurrentLocationProvider(){}
    private static class Holder{
        private static CurrentLocationProvider instance = new CurrentLocationProvider();
    }

    public static CurrentLocationProvider getInstance(){
        return Holder.instance;
    }
    private List<LocationChangeListener> locationChangeListeners = new ArrayList<>();
    public void addListener(LocationChangeListener listener){
        locationChangeListeners.add(listener);
        System.out.println("添加的listener是"+listener+",添加后的集合長(zhǎng)度是:"+locationChangeListeners.size());
    }

    public void removeListener(LocationChangeListener listener){
        locationChangeListeners.remove(listener);
        System.out.println("移除的listener是"+listener+",移除后的集合長(zhǎng)度是:"+locationChangeListeners.size());
    }

    public interface LocationChangeListener{
        void onLocationChange();
    }
}

定義一個(gè)全局的單例,需要監(jiān)聽位置變化的時(shí)候添加監(jiān)聽器,在activity退出的時(shí)候要記得移除掉,否則會(huì)內(nèi)存泄漏。那么我們?cè)贙otlin中怎么使用呢?

fun main() {
    val currentLocationProvider = CurrentLocationProvider.getInstance()
    val listener={

    }

    currentLocationProvider.addListener(listener)
    currentLocationProvider.addListener(listener)
    currentLocationProvider.addListener(listener)
    println()
    currentLocationProvider.removeListener(listener)
    currentLocationProvider.removeListener(listener)
    currentLocationProvider.removeListener(listener)
}

因?yàn)镵otlin會(huì)把Java中單個(gè)抽象函數(shù)的接口作為L(zhǎng)ambda使用,所以這里定義一個(gè)常量listener,那么結(jié)果是怎么樣的呢?看下圖


sam.png

結(jié)果和我們預(yù)想的完全不一樣,從結(jié)果可以看出每一次的add和remove都是一個(gè)新的listener對(duì)象,真的是這樣嗎?我們看一下Kotlin的字節(jié)碼。

...
    LINENUMBER 13 L1
    GETSTATIC com/example/kotlin/sam/SelectPoiKt$main$listener$1.INSTANCE : Lcom/example/kotlin/sam/SelectPoiKt$main$listener$1;
    CHECKCAST kotlin/jvm/functions/Function0
    ASTORE 1
...

這一段是我們定義的listener常量的字節(jié)碼,這里可以看出listener是一個(gè)Function0的實(shí)例。
我們看一下add和remove的字節(jié)碼


add.png

從圖中可以看出每次都會(huì)new一個(gè)匿名實(shí)例實(shí)現(xiàn)LocationChangeListener接口,并且在new 的時(shí)候講Function0的實(shí)例即我們定義的listener傳遞進(jìn)去,然后在add的時(shí)候進(jìn)行類型轉(zhuǎn)換講匿名實(shí)例強(qiáng)轉(zhuǎn)成LocationChangeListener。remove的字節(jié)碼也是這樣實(shí)現(xiàn)的。
所以我們?cè)诿看蝍dd和remove的時(shí)候都是new一個(gè)新的實(shí)例。

那么如果一定要使用Kotlin調(diào)用,怎么解決這個(gè)問(wèn)題了,其實(shí)也很簡(jiǎn)單,我們只要顯示的聲明的實(shí)例就可以了,比如:

fun main() {
    val currentLocationProvider = CurrentLocationProvider.getInstance()

   val listener=object:CurrentLocationProvider.LocationChangeListener{
       override fun onLocationChange() {
       }
   }
    ......
}
此時(shí)我們?cè)趓un運(yùn)行一下就可以看到正確的結(jié)果了。

總結(jié)

這個(gè)地方我們?cè)谶M(jìn)行Java和Kotlin混合開發(fā)的時(shí)候要注意一下。

集合框架的混合開發(fā)的問(wèn)題

我們都知道在Kotlin中的集合分為兩種:只讀和可讀寫的(MutableXXX),所以我們?cè)谶M(jìn)行混合開發(fā)的時(shí)候也要注意這一點(diǎn),比如:

object TestKotlinCollections{
    val list = listOf(1,2,3)
}

Kotlin定義一個(gè)單例,和一個(gè)變量list。注意這里返回是List的實(shí)例,其中List是只讀的。我們?cè)趈ava中一不小心就會(huì)寫出下邊的代碼:

public class JavaTestCollections {
    @Test
    public void testCollections(){
        //這里的list是只讀的不可以寫入
        List<Integer> list = TestKotlinCollections.INSTANCE.getList();
        list.add(4);
    }
}

這段代碼運(yùn)行的時(shí)候回提示你不支持操作異常。

總結(jié)

在混合開發(fā)的時(shí)候要注意Kotlin的代碼返回集合是只讀的還是可讀寫的?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容