Scala:没有明确已知类型参数的类型转换 [英] Scala: Typecast without explicitly known type parameter

查看:42
本文介绍了Scala:没有明确已知类型参数的类型转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下示例:

case class C[T](x:T) {
  def f(t:T) = println(t)
  type ValueType = T
}

val list = List(1 -> C(2), "hello" -> C("goodbye"))

for ((a,b) <- list) {
  b.f(a)
}

在这个例子中,我知道(运行时保证)a 的类型是一些 T,而 b 的类型是 C[T]T 相同.当然,编译器无法知道这一点,因此我们在 b.f(a) 中得到了输入错误.

In this example, I know (runtime guarantee) that the type of a will be some T, and b will have type C[T] with the same T. Of course, the compiler cannot know that, hence we get a typing error in b.f(a).

为了告诉编译器这个调用没问题,我们需要做一个类型转换à la b.f(a.asInstanceOf[T]).不幸的是,T 在这里是未知的.所以我的问题是:我如何重写 b.f(a) 以便编译这段代码?

To tell the compiler that this invocation is OK, we need to do a typecast à la b.f(a.asInstanceOf[T]). Unfortunately, T is not known here. So my question is: How do I rewrite b.f(a) in order to make this code compile?

我正在寻找一种不涉及复杂结构的解决方案(以保持代码可读),并且在某种意义上说是干净的",我们不应该依赖代码擦除来使其工作(请参阅下面的第一种方法).

I am looking for a solution that does not involve complex constructions (to keep the code readable), and that is "clean" in the sense that we should not rely on code erasure to make it work (see the first approach below).

我有一些工作方法,但由于各种原因我发现它们并不令人满意.

I have some working approaches, but I find them unsatisfactory for various reasons.

b.asInstanceOf[C[Any]].f(a)

这是有效的,并且具有合理的可读性,但它基于谎言".b 不是 C[Any] 类型,我们没有得到运行时错误的唯一原因是因为我们依赖于 JVM 的限制(类型擦除).我认为当我们知道 x 实际上是 X 类型时,只使用 x.asInstanceOf[X] 是一种很好的风格.

This works, and is reasonably readable, but it is based on a "lie". b is not of type C[Any], and the only reason we do not get a runtime error is because we rely on the limitations of the JVM (type erasure). I think it is good style only to use x.asInstanceOf[X] when we know that x is really of type X.

  b.f(a.asInstanceOf[b.ValueType])

根据我对类型系统的理解,这应该可以工作.我已将成员 ValueType 添加到类 C 中,以便能够显式引用类型参数 T.然而,在这种方法中,我们得到了一个神秘的错误信息:

This should work according to my understanding of the type system. I have added the member ValueType to the class C in order to be able to explicitly refer to the type parameter T. However, in this approach we get a mysterious error message:

Error:(9, 22) type mismatch;
 found   : b.ValueType
    (which expands to)  _1
 required: _1
  b.f(a.asInstanceOf[b.ValueType])
                    ^

为什么?它似乎抱怨我们期望类型 _1 但得到类型 _1!(但即使这种方法有效,它也仅限于我们有可能将成员 ValueType 添加到 C 的情况.如果 C是一些现有的库类,我们也不能这样做.)

Why? It seems to complain that we expect type _1 but got type _1! (But even if this approach works, it is limited to the cases where we have the possibility to add a member ValueType to C. If C is some existing library class, we cannot do that either.)

for ((a,b) <- list.asInstanceOf[List[(T,C[T]) forSome {type T}]]) {
  b.f(a)
}

这个有效,并且在语义上是正确的(即,我们在调用 asInstanceOf 时不会撒谎").限制是这有点不可读.此外,它对目前的情况有些特殊:如果 a,b 不是来自同一个迭代器,那么我们可以在哪里应用这种类型转换?(此代码还有一个副作用,即对于 Intelli/J IDEA 2016.2 来说过于复杂,在编辑器中将其突出显示为错误.)

This one works, and is semantically correct (i.e., we do not "lie" when invoking asInstanceOf). The limitation is that this is somewhat unreadable. Also, it is somewhat specific to the present situation: if a,b do not come from the same iterator, then where can we apply this type cast? (This code also has the side effect of being too complex for Intelli/J IDEA 2016.2 which highlights it as an error in the editor.)

  val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}]
  b2.f(a2)

我本来希望这个可以工作,因为 a2,b2 现在应该有类型 TC[T] 用于相同的存在T.但是我们得到一个编译错误:

I would have expected this one to work since a2,b2 now should have types T and C[T] for the same existential T. But we get a compile error:

Error:(10, 9) type mismatch;
 found   : a2.type (with underlying type Any)
 required: T
  b2.f(a2)
       ^

为什么?(除此之外,由于创建和销毁一对,该方法的缺点是会产生运行时成本(我认为).)

Why? (Besides that, the approach has the disadvantage of incurring runtime costs (I think) because of the creation and destruction of a pair.)

  b match {
    case b : C[t] => b.f(a.asInstanceOf[t])
  }

这有效.但是用匹配来封闭代码会使代码的可读性大大降低.(而且对于 Intelli/J 来说也太复杂了.)

This works. But enclosing the code with a match makes the code much less readable. (And it also is too complicated for Intelli/J.)

推荐答案

最干净的解决方案是,IMO,您找到的与类型捕获模式匹配的解决方案.您可以通过将模式直接集成到您的理解中来使其简洁,并希望具有可读性,如下所示:

The cleanest solution is, IMO, the one you found with the type-capture pattern match. You can make it concise, and hopefully readable, by integrating the pattern directly inside your for comprehension, as follows:

for ((a, b: C[t]) <- list) {
  b.f(a.asInstanceOf[t])
}

小提琴:http://www.scala-js-fiddle.com/gist/b9030033133ee94e8c10a7777

Fiddle: http://www.scala-js-fiddle.com/gist/b9030033133ee94e8c18ad772f3461a0

如果你还没有理解,不幸的是,相应的模式分配不起作用:

If you are not in a for comprehension already, unfortunately the corresponding pattern assignment does not work:

val (c, d: C[t]) = (a, b)
d.f(c.asInstanceOf[t])

那是因为 t 不在第二行的范围内.在这种情况下,您将不得不使用完整模式匹配.

That's because t is not in scope anymore on the second line. In that case, you would have to use the full pattern matching.

这篇关于Scala:没有明确已知类型参数的类型转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆