为什么Kotlin中的main函数不能声明为lambda形式?
问题原因
Kotlin(以JVM平台为例)的程序入口有严格的编译器约定:只会识别顶层作用域中使用fun关键字声明、命名为main、参数为空或者为Array<String>的函数。
你写的val main : () -> Unit = {}本质不是函数声明,而是一个类型为函数类型的属性声明:它编译后是类中的一个静态字段,类型是kotlin.jvm.functions.Function0<Unit>,存的是lambda实例的引用,本身并不存在JVM方法表中。而JVM启动时要求找的是签名为public static void main(String[] args)的类方法,自然无法识别到这个属性,就会报找不到main方法的错误。
该行为的适用范围
这个问题不止针对main函数,所有需要匹配JVM方法签名的场景都会有同样的限制:
- 给Java侧调用的Kotlin公开方法,只有
fun声明的方法能被Java直接当普通方法调用,函数属性需要Java侧手动调用invoke()方法才能执行 - 作用于方法的注解(比如
@JvmStatic、@Throws、接口请求注解等)只能标注在fun声明的方法上,无法标注在函数类型的属性上
fun声明函数和lambda赋值的函数属性的核心差异
二者本质是完全不同的语言构件,常见的隐性差异包括:
- 底层实现不同:
fun声明的是JVM层面的方法,有固定的方法签名,存储在类的方法表中;lambda赋值给val得到的是函数类型的对象实例,是堆中的对象,本身只有属于FunctionN接口的invoke方法 - 重载能力不同:
fun支持重载,同一作用域内可以存在多个同名、不同参数签名的方法;函数属性不支持重载,同一作用域内不允许出现同名属性,不管类型是否有差异 - 特性支持不同:
fun可以声明为inline、tailrec、operator等,支持修改方法名称的@JvmName等注解;这些特性都无法作用于函数类型的属性 - 调用开销不同:普通
fun调用是方法调用开销,而函数属性的调用默认会产生虚方法调用开销,只有在作为inline函数参数时才会被内联消除开销
内容的提问来源于stack exchange,提问作者Alex M.
相关产品推荐
相关产品推荐

