Kotlin DSL 的 @MyDslMarker 注释的有效目标以及注释函数和属性的目的

问题描述 投票:0回答:1

前提条件

根据设计,

@DslMarker
注释只能应用于
annotation class
。然而,一旦定义了我们自己的注释(例如,
annotation class MyDslMarker
),它就可以不受限制地应用于任何目标。

在传统示例中(例如 Kotlin 文档、HTML 库、其他来源、Stack Overflow 以及几乎所有地方中找到的示例),

@MyDslMarker
注释的主要目标是类(或可能是对象)。

我只能找到一两次提到将其直接应用于函数,而没有提到属性或其他目标。

fun
的一个很好的例子,还解释了颜色样式是如何派生的

常用用法

@DslMarker
专门设计用于限制从内部 lambda 内部对外部上下文接收器的隐式访问。它通常间接应用于表示上下文的类。

如果上下文类属于第三方库(您无法直接对其应用注释),您将按如下方式处理它(简化):

@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) // Added a target "TYPE"
@DslMarker
annotation class MyDslMarker 

// Can't apply the annotation here because it's outside our codebase
class ThirdPartyClassContext {
    fun doPrint() = println("Third-party")
}

@MyDslMarker
class OuterContext {

    // Applying to the receiver type
    fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
        ThirdPartyClassContext().init()
        //...
    }
}

@MyDslMarker
class OuterThing

fun outerThing(init: OuterContext.() -> Unit): OuterThing {
    val context = OuterContext()
    context.init()
    return OuterThing()
    // in the real code, of course, it would be more like:
    // return OuterThing(context), 
    // but that is beside the point
}

fun main() {
    outerThing {
        third {
            doPrint()
            // As desired: 'fun third(init: (ThirdPartyClassContext).() -> Unit): Unit'
            // cannot be called implicitly in this context due to the receiver restriction
            third { }
        }
    }
}

这是一个常见的用例,并且经常被涵盖 - 例如,如此处所述。

但是,这是

@MyDslMarker
仅有的两种经常讨论的使用场景,没有可靠的来源说明其在函数、属性或其他目标上的应用。

为什么这看起来很重要

  1. 由于文档中既没有禁止也没有特别提及,因此在函数、属性甚至其他目标上使用此类注释可能具有有效的用例,类似于注释

    ThirdPartyClass
    类型的场景。

  2. 当我将

    @MyDslMarker
    应用于
    Context
    类中的方法,然后使用该方法时,它会显示为紫色(再次,有关颜色的更多详细信息请参见此处)。这表明:

fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
    ThirdPartyClassContext().init()
}

How it looks in the IDE

@MyDslMarker
fun third(init: (@MyDslMarker ThirdPartyClassContext).() -> Unit) {
    ThirdPartyClassContext().init()
}

How it looks in the IDE

有些不同。

最后的问题

  1. 除了颜色之外,上面的示例中实际上还有什么变化吗?
  2. 如果确实发生变化,用例是什么?
  3. 虽然理论上我们可以将其应用于任何可用的目标(例如函数、属性、属性获取器等),但这是否可行?如果可行,以什么方式实现?
// At a minimum, these targets are available:
@Target(
    AnnotationTarget.CLASS, 
    AnnotationTarget.TYPE, 
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY, 
    AnnotationTarget.PROPERTY_GETTER
)
@DslMarker
annotation class MyDslMarker

不幸的是,官方文档和相关示例充其量是稀疏且缺乏信息的。

感谢您的任何见解!我相信这些问题的答案不仅对我有帮助,而且对许多其他正在搜索相关信息的人都有帮助,特别是因为 JetBrains 团队本身并没有提供太多说明。


kotlin annotations dsl kotlin-dsl
1个回答
0
投票

除了颜色之外,上面的示例中实际上还有什么变化吗?

着色只是 IntelliJ 语法突出显示的一部分。它的目的只是突出显示哪些调用是 DSL 方法调用。它还允许您自定义 DSL 标记应使用的颜色,从而区分来自不同 DSL 的方法调用。您可以通过单击此装订线图标来完成此操作:

enter image description here

另请参阅:Kotlin - DSL 颜色样式 以及此功能的测试数据

虽然理论上我们可以将其应用于任何可用的目标(例如函数、属性、属性获取器等),但这是否可行?如果可行,以什么方式实现?

IntelliJ 已经利用它来进行语法突出显示,正如我刚才所描述的。 IntelliJ 的这一功能不仅限于函数,还包括属性和对象声明。请参阅此目录中的其他文件以查看一些示例。

然而,就语言而言,只有应用于 types 的 DSL 标记才有效(至少目前如此)。您可以在 DslMarkerUtils.kt 中找到编译器如何处理此问题。该文件是编译器查找

@DslMarker
注释的唯一文件。
extractDslMarkerFqNames
仅从
KotlinType
中提取 DSL 标记:

fun extractDslMarkerFqNames(kotlinType: KotlinType): Set<FqName> {
    val result = mutableSetOf<FqName>()
    result.addAll(kotlinType.annotations.extractDslMarkerFqNames())

    kotlinType.getAbbreviation()?.constructor?.declarationDescriptor?.run {
        result.addAll(annotations.extractDslMarkerFqNames())
        (this as? TypeAliasDescriptor)?.run {
            result.addAll(extractDslMarkerFqNames(this.underlyingType))
        }
    }

    kotlinType.constructor.declarationDescriptor?.getAllSuperClassifiers()?.asIterable()
        ?.flatMapTo(result) { it.annotations.extractDslMarkerFqNames() }

    return result
}

您可以准确地看到它如何找到

@DslMarker
注释,例如
@MyDslMarker
:

  • 直接查看类型上的注释,例如
    @MyDslMarker ThirdPartyClassContext
  • 查看该类型声明上的注释,例如
    OuterContext
    。它还在类型的超类中查找此类注释
  • 如果该类型是类型别名,则在其别名的实际类型上递归地查找此类注释。

此方法由另一个采用

extractDslMarkerFqNames
ReceiverValue
方法调用,然后由 DslScopeViolationCallChecker.kt 使用。

© www.soinside.com 2019 - 2024. All rights reserved.