假设我有一个如下所示的扩展接口:

interface MyExtension {
    abstract val container: ExtensiblePolymorphicDomainObjectContainer<MyBaseClass>
}

当我try 通过以下代码创建此扩展的实例时:

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val extension = project.extensions.create<MyExtension>("myExtension")
    }
}

似乎Gradle(至少在7.6.1版本中)会因为无法创建扩展而遇到应用MyPlugin的问题,而这又是因为能够正确实现container属性.

这是否是Gradle或Kotlin DSL需要解决方法的限制(例如,将MyExtension设置为抽象类并专门注入ObjectFactory),还是我做错了什么?

推荐答案

Gradle can only create specific types of properties on managed types

这些是

  • Property<T>
  • RegularFileProperty
  • DirectoryProperty
  • ListProperty<T>
  • SetProperty<T>
  • MapProperty<K, V>
  • ConfigurableFileCollection
  • ConfigurableFileTree
  • DomainObjectSet<T>
  • NamedDomainObjectContainer<T>

Gradle v8.2,将支持ExtensiblePolymorphicDomainObjectContainer<T>.在此之前,必须使用ObjectFactory手动创建它们.

实现这一点的一种方法是将ObtFactory注入构造函数:

import javax.inject.Inject
import org.gradle.api.*
imports org.gradle.kotlin.dsl.*

abstract class MyExtension @Inject constructor(
  private val objects: ObjectFactory,
) {
  val container: ExtensiblePolymorphicDomainObjectContainer<MyBaseClass> =
    objects.polymorphicDomainObjectContainer()
}

Pro tip: Kotlin DSL accessors for NamedDomainObjectContainers

请注意,尽管Gradle会自动创建NamedDomainObjectContainer个属性,但手动创建一个属性并将其作为扩展添加通常是个好主意.

abstract class MyExtension @Inject constructor(
  private val objects: ObjectFactory,
) : ExtensionAware {
// *all* types that Gradle instantiates are automatically ExtensionAware,
// but adding the interface explicitly helps with IDE hints
  
  val namedContainer: NamedDomainObjectContainer<MyBaseClass> =
    objects.domainObjectContainer(MyBaseClass::class).also {
      extensions.add("namedContainer", it)
    }
}

这是冗长和奇怪的--所以为什么要麻烦呢?

通过添加namedContainer作为扩展,Gradle将自动为添加的任何元素生成一些不错的type-safe Kotlin DSL accessors.

因此,如果在您的插件中添加了一个元素.

abstract class MyPlugin : Plugin<Project> {
  override fun apply(project: Project) {
    val extension = project.extensions.create<MyExtension>("myExtension")
    extension.namedContainer.register("anElement") { ... }
  }
}

在任何构建脚本中,都有一种以类型安全和IDE友好的方式访问元素的简单方法

// build.gradle.kts
plugins {
  id("my.plugin")
}

myExtension {
  namedContainer.anElement { // auto generated accessor
  }
}

Kotlin相关问答推荐

如何在操作系统版本上正确获取Room数据库的路径>;=26 sdk?

kotlin - 挂起简单方法调用链时可能存在冗余分支

如何在 Kotlin 中初始化 Short 数组?

使用 kotlin 流删除 map 中具有某些相似性的值

为什么 Kotlin 在 sumOf 函数 lambda 中默认不将数字视为Int?

在 Kotlin 中使用 @Parcelize 注释时如何忽略字段

在 Scaffold Jetpack Compose 内的特定屏幕上隐藏顶部和底部导航器

Kotlin 单元测试 - 如何模拟 Companion 对象的组件?

为什么 Dialog 没有 NavController [Missing]?

使用 Paging 3 时保存并保留 LazyColumn 滚动位置

@uncheckedVariance 在 Kotlin 中?

如何在协程之外获取 Flow 的值?

TornadoFX 中设置 PrimaryStage 或 Scene 属性的方法

类型不匹配:推断类型为 LoginActivity 但应为 LifecycleOwner

Kotlin:具有多个不同类型设置器的单个属性

Kotlin 与 C# 中的标志枚举变量具有相似效果的方式是什么

Kotlin中的属性(properties)和参数(parameters)有什么区别?

在 sharedPref.getString 中有一个默认值有什么意义?

安装 Kotlin-Jupyter:e: java.lang.NoClassDefFoundError: 无法初始化类 org.jetbrains.kotlin.com.intellij.pom.java.LanguageLevel

Kotlin 对象 vs 伴生对象(companion-object) vs 包作用域(package scoped)方法