【发布时间】:2022-01-09 23:34:03
【问题描述】:
我会简化让我走到这一步的问题,但我正在考虑这段代码:
abstract class A {
fun myCustomApply(block: A.() -> Unit): A {
this.block() // block() would have the same effect
// Do something else here
return this
}
}
class B : A() {
fun anotherFunction() {
// Do something
}
}
这段代码看起来(并且)没问题。但请考虑以下 sn-p :
fun doSomethingVeryUseful(): B /* Won't work */ {
val b = B()
return b.myCustomApply {
anotherFunction() // Nope
}
}
在这里,编译器会抱怨,原因有两个,乍一看可能并不明显。首先myCustomApply的block参数是用A.() -> Unit类型声明的,所以我们不能在其作用域内调用anotherFunction(),因为它是在B类中声明的。
然后,doSomethingVeryUseful 的返回类型似乎被声明为 B,但 myCustomApply 返回 A,所以我们必须使用 as A 强制转换。
这两个错误都是合乎逻辑的,因为编译器无法自行猜测“哦,是的,返回类型是 A,但它总是返回 this,所以这样做完全没问题!”,但这些有点痛苦正确处理,因为我个人想不出合适的解决方法。
当然,你可以这样做:
abstract class A
class B : A() {
fun anotherFunction() {
// Do something
}
}
fun <T : A> T.myCustomApply(block: T.() -> Unit): T {
this.block()
// Do something else
return this
}
但我个人认为这不是一个好主意,因为它混淆了代码(myCustomApply 来自哪里?为什么不在 A 类中?),并且对于这样一个简单的问题有点复杂。你可以认为这是一个很好的解决方案,但我并不同意。
我的想法是做这样的事情(不要在你的 IDE 中尝试这个,它只会抱怨你是有史以来最糟糕的开发者,你甚至不应该存在,因为它不是实际的语法) :
abstract class A {
fun myCustomApply(block: this.() -> Unit): this {
block()
// Do something else
return this
}
}
class B : A() {
fun anotherFunction() {
// Do something
}
}
fun doSomethingVeryUseful(): B {
val b = B()
return b.myCustomApply {
anotherFunction() // YEAH
}
}
请注意,this 用于两个地方:作为接收器类型和返回类型。
由于this 已经是关键字,它不会破坏任何现有代码,因此保证了向后兼容性。
this 作为一个类型将有一个可接受的值,即this。为什么使用this 作为类型?这将向编译器保证该值实际上是接收器的类型,即 b.myCustomApply 现在接受 B.() -> Unit 类型的 lambda,并具有 B 类型的返回类型,因为 b 本身是类型B.
另一个(纯粹的美学)优势是,现在很清楚同一个对象(即接收者)将被传递给 lambda,然后返回给方法的调用者。
这将转化为这样的东西:
abstract class A<_this : A<_this>> {
private fun _this(): _this =
@Suppress("UNCHECKED_CAST") (this as _this)
fun myCustomApply(block: _this.() -> Unit): _this {
_this().block()
// You got the idea
return _this()
}
}
class B : A<B>() {
fun anotherFunction() {
// Do something
}
}
_this 类型参数的名称很容易解释它的作用,如果 A 已经有类型参数,那么应该在它们的开头添加 _this,以及任何形式为 A
this 作为返回类型将要求 return this 和 return /* something that has this as type */ 是唯一允许的 return 语句,因为我们无法确保任何其他值实际上总是与 @987654348 具有相同类型@。
我建议this(多么棒的双关语啊),因为我有好几次觉得需要这样一个功能,但我不确定这是否是个好主意,如果可行的话,以及是否真的有人感兴趣。
无论哪种情况,感谢您的阅读,祝您有美好的一天!
【问题讨论】:
-
嗨 - 你是在寻求帮助来解决问题吗?我不清楚你想要达到什么目标。看来您有一个解决方法(尽管我认为可以使用泛型对其进行改进)。但是首先你能阅读关于如何提问的 StackOverflow 指南,然后更新你的帖子吗?如果你想提出一个新的 Kotlin 功能,你在 JetBrains YouTrack 上搜索过吗?最后,接收器的改进即将到来,您可能感兴趣:youtrack.jetbrains.com/issue/KT-42435
-
SO 是一个问答网站。您不是在问问题,而是在提出语言功能。 Kotlin 开源项目和/或 slack 组将是一个更好的地方。
-
啊,所以现在您编辑了问题以添加“我的”答案,所以我的答案看起来很傻。太好了。
-
感谢您的回答(这不是真正的答案,因为我的问题不是真正的问题)。我看到这已经被要求了(这并不让我感到惊讶),所以我不会在 ASemy 发送的链接中询问它(但谢谢!)。我忘记了 Stackoverflow 只是来问问题的,所以我会压制它,因为它是一个提议(我只是想知道你对此的看法)!
-
我投票结束这个问题,因为这不是一个问题,并且更适合 Kotlin 论坛