报告 扩展程序中不正确的伴生对象用法。

Kotlin 伴生对象始终会在您尝试加载其包含类时创建,并且扩展点实现的创建开销应该很低。 插件中过多的类加载是 IDE 启动的一个大问题。

错误模式:


  class KotlinDocumentationProvider : AbstractDocumentationProvider(), ExternalDocumentationProvider {

      companion object {
          private val LOG = Logger.getInstance(KotlinDocumentationProvider::class.java)
          private val javaDocumentProvider = JavaDocumentationProvider()
      }
  }

这里 KotlinDocumentationProvider 是在 plugin.xml 中注册的扩展:


  <lang.documentationProvider language="JAVA"
                              implementationClass="org.jetbrains.kotlin.idea.KotlinDocumentationProvider"
                              order="first"/>

在此示例中,只要有人调用 new KotlinDocumentationProvider(),就会从磁盘加载 JavaDocumentationProvider

扩展点实现中的 Kotlin 伴生对象只能包含记录器和简单常量。 其他声明可能会导致过多类加载或提前初始化开销大的资源(TokenSet、Regex 等)。 加载扩展类时。 请注意,即使标记为 @JvmStatic 的声明也会为伴生对象生成一个额外的类,这可能会导致开销巨大的计算。

这些声明必须存储在对象中或处于顶层,而不是存储在伴生对象中。

常见问题解答

如何重写运行 ConfigurationType?

将声明移到顶层:


  // 正确做法,使用顶层 fun
  internal fun mnRunConfigurationType(): MnRunConfigurationType = runConfigurationType<MnRunConfigurationType>()

  internal class MnRunConfigurationType : ConfigurationType {
    companion object { // 错误做法
      fun getInstance(): MnRunConfigurationType = runConfigurationType<MnRunConfigurationType>()
    }
    ...

如何重写 FileType?

之前:


  internal class SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {
    companion object {
      val FILE_TYPE = SpringBootImportsFileType()
      ...

之后:


  internal object SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {
  ...

plugin.xml 中使用 INSTANCE fieldName:


  <fileType name="Spring Boot Imports"
              fieldName="INSTANCE"
              implementationClass="com.intellij.spring.boot.spi.SpringBootImportsFileType"/>

如何重写 rewrite CounterUsagesCollector?

内部 API

之前:


  class AntActionsUsagesCollector : CounterUsagesCollector() {
    override fun getGroup(): EventLogGroup = GROUP

    companion object {
      private val GROUP = EventLogGroup("build.ant.actions", 1)

      @JvmField
      val runSelectedBuildAction = GROUP.registerEvent("RunSelectedBuild")
   }
}

之后:


 object AntActionsUsagesCollector : CounterUsagesCollector() {
  override fun getGroup(): EventLogGroup = GROUP

  private val GROUP = EventLogGroup("build.ant.actions", 1)

  @JvmField
  val runSelectedBuildAction = GROUP.registerEvent("RunSelectedBuild")
}

2023.3 最新变化