为什么在我的通用 Kotlin 函数中进行类型转换时没有检查?

huangapple go评论66阅读模式
英文:

Why is this cast in my generic Kotlin function unchecked?

问题

把这个 MVCE 看一下:

abstract class Bar

class BarHolder(val barFunctor: Bar.() -> Unit)

fun <T : Bar> foo(bar: T.() -> Unit) {
    val activeRequest = BarHolder(bar as (Bar.() -> Unit))
}

这里的强制类型转换 bar as (Bar.() -> Unit) 被标记为未检查。但是我想不出在运行时会出现强制转换失败的情况,因为 T 总是 Bar 的子类,对吗?

我知道可以通过添加 @Suppress("UNCHECKED_CAST") 来忽略它,但我认为这是不好的做法。那么避免不安全转换的正确方法是什么?

为了更多的上下文,预期的用例类似于这样:

class LongBar : Bar() {
    val length = 69
}

fun foobar() {
    foo<LongBar> {
        println(length)
    }
}
英文:

Take a look at this MVCE:

abstract class Bar

class BarHolder(val barFunctor : Bar.()-&gt; Unit)

fun &lt;T : Bar&gt;foo(bar: T.() -&gt; Unit) {
    val activeRequest = BarHolder(bar as (Bar.() -&gt; Unit))
}

The cast bar as (Bar.() -&gt; Unit) is marked as unchecked. But I can't think of a scenario where the cast fails at runtime, since T is always a subclass of Bar, isn't it?

I know I can ignore it by adding @Suppress(&quot;UNCHECKED_CAST&quot;) but this seems bad practice to me. So what is the right way to avoid the unsafe cast?

For more context, the intended use case looks similar to this:

class LongBar : Bar()
    val length = 69

fun foobar(){
    foo&lt;LongBar&gt;(){
        println(length)
    }
}

答案1

得分: 4

你的代码不是类型安全的,而且 Kotlin 是正确的,不应该允许这样做。

问题在于你可能有一个特定类型的 Bar 的消费者,例如 Bar1,然后将其转换为任何 Bar 对象的消费者。如果你然后使用这个 Bar1 的消费者并将一个 Bar2 对象传递给它会发生什么呢?

fun main() {
    val bar1Consumer: Bar1.() -> Unit = { toString() }
    val holder = foo(bar1Consumer)
    holder.barFunctor.invoke(Bar2()) // ClassCastException
}

fun <T : Bar> foo(bar: T.() -> Unit) = BarHolder(bar as (Bar.() -> Unit))

class Bar1 : Bar()
class Bar2 : Bar()

很难与你的真实示例相关联,因为我们不知道 foo() 中到底发生了什么,但请注意,在进行了不受检查的强制类型转换之后,以下操作是允许的并将引发 ClassCastException

val activeRequest = BarHolder(bar as (Bar.() -> Unit))
activeRequest.barFunctor.invoke(IntBar())
英文:

Your code is not type-safe and Kotlin is correct it should not be allowed.

The problem is that you may have a consumer of a very specific type of Bar, for example Bar1 and then you cast it to consumer of any Bar objects. What if you then use this consumer of Bar1 and pass a Bar2 object to it?

fun main() {
    val bar1Consumer: Bar1.() -&gt; Unit = { toString() }
    val holder = foo(bar1Consumer)
    holder.barFunctor.invoke(Bar2()) // ClassCastException
}

fun &lt;T : Bar&gt;foo(bar: T.() -&gt; Unit) = BarHolder(bar as (Bar.() -&gt; Unit))

class Bar1 : Bar()
class Bar2 : Bar()

It is hard to relate to your real example as we don't know what exactly happens in foo(), but please note after that unchecked cast this is allowed and will throw the ClassCastException:

val activeRequest = BarHolder(bar as (Bar.() -&gt; Unit))
activeRequest.barFunctor.invoke(IntBar())

huangapple
  • 本文由 发表于 2023年6月19日 04:00:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502342.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定