Kotlin和Java在匿名内部类和接口的使用中的区别及对比
1、Kotlin中使用匿名内部类和接口:
interface ITest {fun test(param: Int) {Log.e("zxy99999", "::ITest:test:param:$param")}fun test222(param: Int) {Log.e("zxy99999", "::ITest:test222:param:$param")}
}var mITest: ITest? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)initTest()btn.setOnClickListener {mITest.test()}btn222.setOnClickListener {mITest.test222()}}private fun initTest() {var count = 0/***** 一、在匿名内部类中使用局部变量 *****/mITest = object: ITest {override fun test(param: Int) {count++Log.e("zxy99999", "::initTest 回调test方法,count:$count, param:$param")super.test(param + count)}override fun test222(param: Int) {count++Log.e("zxy99999", "::initTest 回调test222方法,count:$count, param:$param")super.test222(param + count)}}/***** 二、为了观察局部变量的变化,在延时任务中使用局部变量 *****/ Handler().postDelayed({count = count + 300Log.e("zxy99999", "::5秒之后 initTest count:$count")}, 10000)
}
2、Java中使用匿名内部类和接口:
public class TestObj {abstract class ITest {public void test(int param) {Log.e("zxy99999", "::ITest:test:param:" + param);}public void test222(int param) {Log.e("zxy99999", "::ITest:test222:param:" + param);}}ITest mITest = null;public TestObj() {super();}int outCount = 0;public void initTest() {int count = 0;mITest = new ITest() {@Overridepublic void test(int param) {outCount++;count++; // 会报错 Variable 'count' is accessed from within inner class, needs to be final or effectively finalsuper.test(param + count);}@Overridepublic void test222(int param) {super.test222(param);}};new Handler().postDelayed(new Runnable(){@Overridepublic void run() {outCount = outCount + 300;count = count + 300; // 会报错,Variable 'count' is accessed from within inner class, needs to be final or effectively final}}, 10000);}
}
区别一:接口
Kotlin 的接口中可以直接实现方法,这是 Kotlin 接口与 Java 早期版本接口的重要区别(Java的接口中是不能实现方法的,Java 8+ 才通过 default 关键字支持类似功能)。
Kotlin 接口中方法的两种形式
- 抽象方法(无实现)只声明方法签名,没有方法体,必须由实现接口的类重写。
- 具体方法(有实现)直接定义方法体,实现接口的类可以直接继承使用,也可以选择重写。
区别二:匿名内部类中引用方法中的局部变量
在 Kotlin 中,当局部变量被匿名内部类或 lambda 捕获并修改时(如代码中的 count),编译器会将其包装为堆内存中的可变对象,此时不会再对原局部变量保存副本,而是通过共享这个包装对象实现变量的统一访问。
具体原因分析
- 包装的本质是 “共享容器”编译器生成的包装对象(可理解为一个持有 count 实际值的容器)是唯一的,匿名内部类、延迟任务以及原方法中的 count 引用,最终指向的是同一个包装对象。
原方法中对 count 的修改(如 count = count + 300),本质是修改包装对象内部的值。
匿名内部类中对 count 的修改(如 count++),也是修改同一个包装对象内部的值。
因此,不存在 “副本”,所有操作都作用于同一个数据源。 - 与 Java 中 “不可变局部变量” 的区别在 Java 中,若局部变量被匿名类捕获且不可变(final),编译器会为匿名类生成一个值的副本(因为变量不会被修改,副本不影响一致性)。但 Kotlin 中允许捕获可变局部变量(无需 final),此时必须通过包装对象实现共享修改,而不是副本(否则会导致原变量和匿名类中的副本值不一致)。
以上解释来自豆包。