extension属性默认值必须在无参构造函数中设置,不可依赖字段初始化;kotlin中禁用data class和字段默认值,应使用普通class配无参构造或@inject构造函数,并避免原始集合类型,改用property确保延迟初始化与类型安全。

Extension 属性默认值必须在构造函数里设,不能靠字段初始化
Gradle 的 Extension 实例不是普通 Java/Kotlin 对象,它由 Gradle 运行时通过反射 + 动态代理创建。如果你只写 def name = "perry" 或 String name = "perry",这个值在 DSL 解析前就丢失了——因为 Gradle 创建的是代理对象,字段初始化语句根本不会执行。
正确做法是把默认值写进无参构造函数里:
class FreeCoderExtension {
String name
Boolean enableFeature
FreeCoderExtension() {
this.name = "perry"
this.enableFeature = true
}
}
这样 DSL 未显式配置时,project.extensions.getByType(FreeCoderExtension.class).name 才会返回 "perry",而不是 null。
用 Kotlin 写 Extension 时要小心 data class 和默认参数陷阱
Kotlin 的 data class 看似方便,但 Gradle 不支持它作为 Extension 类型——构造函数会被代理绕过,所有默认参数失效。同样,带默认参数的普通 class 构造函数也靠不住,因为 Gradle 调用的是无参构造器(即使你没显式定义)。
安全写法只有两种:
- 用普通
class,明确定义无参构造函数,并在其中赋默认值 - 用
@javax.inject.Inject构造函数(需配合gradleApi()且 Gradle 版本 ≥ 7.4),Gradle 会调用它并传入Project等上下文
别碰 data class,也别依赖 Kotlin 的 val name: String = "perry" 这类字段级默认值。
DSL 配置块里没写的属性,不一定等于 null —— 取决于类型和 getter
比如你声明了一个 List<string> excludes</string>,但用户没在 myPlugin { } 里配 excludes,那它不会是空集合,而是 null。直接调用 excludes.each{} 就会 NPE。
更健壮的做法是:
- 在构造函数里初始化为不可变空集合:
this.excludes = Collections.emptyList() - 或提供带防御性判断的 getter:
getExcludes() { excludes ?: emptyList() } - 避免用原始集合类型,改用
Property<list>></list>(Gradle 6.1+),它天然支持延迟初始化和默认值
IDE 不提示、gradle properties 查不到?大概率是 Extension 没被正确注册
常见错误包括:
- 在
apply()里用project.extensions.add("myPlugin", new MyExtension())—— 这注册的是实例,不是类型,DSL 无法绑定 - 忘记在
resources/META-INF/gradle-plugins/myplugin.properties里写implementation-class=com.example.MyPlugin - 插件类没实现
Plugin<project></project>,或用了 Kotlin 却没加@Suppress("unused")防止内联优化掉构造函数
只要注册方式不对,哪怕默认值写得再对,DSL 也压根不会触发 Extension 初始化,gradle properties 自然看不到你的配置项。











