gpt4 book ai didi

kotlin - 在 Kotlin DSL 构建器中控制范围

转载 作者:行者123 更新时间:2023-12-02 13:08:38 27 4
gpt4 key购买 nike

我试图为我的范围问题找到完美的解决方案,我真的很想听听你的意见。

我有一些无法更改的第三方类(class):

class Employee {
var id = 0
var name = ""
var card : Card? = null
// ...
}

class Card {
var cardId = 0
}

我的目标是能够建立这样的员工:
val built = employee {
id = 5
name = "max"
addCard {
cardId = 5
}
}

没有方法 添加卡在原来的 bean 里。
因此,我想出了以下构建器:
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Scoped

@Scoped
object Builder {
inline fun employee (init: (@Scoped Employee).() -> Unit): Employee {
val e = Employee()
e.init()
return e
}

inline fun Employee.addCard(init: (@Scoped Card).() -> Unit) {
val c = Card()
c.init()
card = c
}
}

不幸的是,现在我得到了臭名昭著的错误:

error: 'inline fun Employee.addCard(init: (Scratch_1.Card).() -> Unit): Unit' can't be called in this context by implicit receiver. Use the explicit one if necessary



我了解错误的原因,我想考虑解决方案。
  • 删除 DSLMarker 注释以能够继承父范围。不幸的是,这允许非法使用构建器:
    with(Builder) {
    val built = employee {
    id = 5
    name = "max"
    addCard {
    employee {
    // ...
    }
    cardId = 5
    }
    }
    }
  • 使用限定的 this 来访问父作用域。但是我们必须使用另一个合格的 this 来获得正确的接收器。这很冗长。
    with(Builder) {
    val built = employee {
    id = 5
    name = "max"
    with(this@with) {
    this@employee.addCard {
    cardId = 5
    }
    }
    }
    }
  • 继承员工可以把扩展功能放在里面(委托(delegate)在这里是不可能的,因为我在Employee中有很多属性,并且没有全部由接口(interface)定义)。
    如果第三方类是最终的,这并不总是有效。
    class EmployeeEx : Employee() {
    inline fun addCard(init: (@Scoped Card).() -> Unit) {
    val c = Card()
    c.init()
    card = c
    }
    }

    和 build 者:
    @Scoped
    object Builder {
    inline fun employee (init: (@Scoped EmployeeEx).() -> Unit): Employee {
    val e = EmployeeEx()
    e.init()
    return e
    }
    }


  • 那么最好的解决方案是什么?我错过了什么吗?
    非常感谢您阅读所有这些!

    最佳答案

  • 您可以定义extension function没有产生新的类(Class),
    它也适用于外国不可接触的来源:
  • @DslMarker
    @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
    annotation class Scoped

    @Scoped
    object Builder {
    inline fun employee(init: (@Scoped Employee).() -> Unit) = Employee().apply(init)
    }

    fun Employee.addCard(init: (@Scoped Card).() -> Unit) = run { card = Card().apply(init) }
  • 控制 dsl 范围的经典工具有两种:
  • @DSLMarker对于您正在使用的可实现的代码,以及
  • @Deprecated (level = ERROR)对于第一种方法不起作用的所有其他情况。

  • 例如,目前您可以构建嵌入式员工:
    val built = employee {
    id = 5
    name = "max"
    addCard {
    cardId = 6
    }
    employee { } // <--- compilable, but does not have sense
    }

    您可以通过弃用的方式直接禁止此操作:
    @Deprecated(level = DeprecationLevel.ERROR, message = "No subcontractors, please.")
    fun Employee.employee(init: (@Scoped Employee).() -> Unit) = Unit

    现在以下示例不可编译:
     val built = employee {
    //...
    employee { } // <--- compilation error with your message
    }
  • 您可能会发现这很有用:Kotlin DSL example
  • 关于kotlin - 在 Kotlin DSL 构建器中控制范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55684541/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com