美文网首页
Kotlin中遇到的问题

Kotlin中遇到的问题

作者: 烧伤的火柴 | 来源:发表于2020-06-26 11:41 被阅读0次

介绍

这里记录一下使用Kotlin中遇到的一些问题。

Lambda问题

我们在开发App的时候经常会使用观察者模式订阅某个主题,当主题发生变化的时候通知所有观察者,但是在Java中定义观察者的逻辑后,在Kotlin中调用Java的时候就会出现奇怪的问题。比如:

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+",添加后的集合长度是:"+locationChangeListeners.size());
    }

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

    public interface LocationChangeListener{
        void onLocationChange();
    }
}

定义一个全局的单例,需要监听位置变化的时候添加监听器,在activity退出的时候要记得移除掉,否则会内存泄漏。那么我们在Kotlin中怎么使用呢?

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)
}

因为Kotlin会把Java中单个抽象函数的接口作为Lambda使用,所以这里定义一个常量listener,那么结果是怎么样的呢?看下图


sam.png

结果和我们预想的完全不一样,从结果可以看出每一次的add和remove都是一个新的listener对象,真的是这样吗?我们看一下Kotlin的字节码。

...
    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常量的字节码,这里可以看出listener是一个Function0的实例。
我们看一下add和remove的字节码


add.png

从图中可以看出每次都会new一个匿名实例实现LocationChangeListener接口,并且在new 的时候讲Function0的实例即我们定义的listener传递进去,然后在add的时候进行类型转换讲匿名实例强转成LocationChangeListener。remove的字节码也是这样实现的。
所以我们在每次add和remove的时候都是new一个新的实例。

那么如果一定要使用Kotlin调用,怎么解决这个问题了,其实也很简单,我们只要显示的声明的实例就可以了,比如:

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

   val listener=object:CurrentLocationProvider.LocationChangeListener{
       override fun onLocationChange() {
       }
   }
    ......
}
此时我们在run运行一下就可以看到正确的结果了。

总结

这个地方我们在进行Java和Kotlin混合开发的时候要注意一下。

集合框架的混合开发的问题

我们都知道在Kotlin中的集合分为两种:只读和可读写的(MutableXXX),所以我们在进行混合开发的时候也要注意这一点,比如:

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

Kotlin定义一个单例,和一个变量list。注意这里返回是List的实例,其中List是只读的。我们在java中一不小心就会写出下边的代码:

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

这段代码运行的时候回提示你不支持操作异常。

总结

在混合开发的时候要注意Kotlin的代码返回集合是只读的还是可读写的?

相关文章

网友评论

      本文标题:Kotlin中遇到的问题

      本文链接:https://www.haomeiwen.com/subject/qfiyfktx.html