介紹
這里記錄一下使用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é)果是怎么樣的呢?看下圖

結(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é)碼

從圖中可以看出每次都會(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的代碼返回集合是只讀的還是可讀寫的?