我试图找到我的范围问题的完美解决方案,我真的很想你的意见。
我有一些我无法改变的第三方课程:
class Employee {
var id = 0
var name = ""
var card : Card? = null
// ...
}
class Card {
var cardId = 0
}
我的目标是能够像这样构建一个Employee:
val built = employee {
id = 5
name = "max"
addCard {
cardId = 5
}
}
原始bean中没有addCard方法。因此,我提出了以下构建器:
@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
}
}
不幸的是,现在我得到了臭名昭着的错误:
错误:'内联乐趣Employee.addCard(init:(Scratch_1.Card)。() - > Unit):无法通过隐式接收器在此上下文中调用单元'。如有必要,请使用显式的
我理解错误的原因,我想考虑解决方案。
with(Builder) {
val built = employee {
id = 5
name = "max"
addCard {
employee {
// ...
}
cardId = 5
}
}
}
with(Builder) {
val built = employee {
id = 5
name = "max"
with(this@with) {
[email protected] {
cardId = 5
}
}
}
}
class EmployeeEx : Employee() {
inline fun addCard(init: (@Scoped Card).() -> Unit) {
val c = Card()
c.init()
card = c
}
}
和建设者:
@Scoped
object Builder {
inline fun employee (init: (@Scoped EmployeeEx).() -> Unit): Employee {
val e = EmployeeEx()
e.init()
return e
}
}
那么什么是最好的解决方案?我错过了什么吗?非常感谢阅读这一切!
@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) }
@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
}
我提供以下设计,它非常经典,结果代码很短。
Builder
增加了额外的范围并阻止我们进口丑陋,我们只是停止使用with
构造并重载invoke
运算符。@DslMarker
用于可编辑代码,将@Deprecated
用于外部代码以控制范围@Scoped
object builder {
operator fun invoke(init: BuildingContext.() -> Unit) = BuildingContext().init()
}
@Scoped
class BuildingContext {
fun employee(init: Employee.() -> Unit) = Employee().apply { init() }
fun Employee.addCard(init: Card.() -> Unit) = run {card = Card().apply { init() }}
@Deprecated(level = DeprecationLevel.ERROR, message = "Employee is forbidden here.")
fun Employee.employee(init: (@Scoped Employee).() -> Unit) { }
@Deprecated(level = DeprecationLevel.ERROR, message = "Card is forbidden here.")
fun Card.addCard(init: (@Scoped Card).() -> Unit) { }
}
fun main(args: Array<String>) {
builder {
val crafted = employee {
//employee {} <-- deprecated, causes compilation error
id = 5
name = "max"
addCard {
// addCard {} <-- deprecated too
cardId = 7
}
}
println(crafted.card?.cardId)
}
}
完整版本在这里工作:https://pl.kotl.in/ICLYZyetU
好的,我想我现在有一个很好的概述。
首先,我认为问题的原因是构建器对象上的IScoped。删除它,它的工作原理。但它仍然允许“非法”语法:
val built = employe {
id = 5
name = "max"
addCard {
employe {
}
cardId = 5
}
}
解决方案
只保留构建器对象中的扩展方法,不要在后面添加注释。
就我而言,我必须引入另一个构建器来开始构建
object EmployeBuilder {
}
object Builder {
inline fun EmployeBuilder.employe(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
}
}
fun main() {
with(Builder) {
val built = EmployeBuilder.employe {
id = 5
name = "max"
addCard {
cardId = 5
}
}
}
}
现在我们拥有它: