Scala:没有明确知道类型参数的Typecast

Dom*_*ruh 10 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)
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我试过的方法:

b.asInstanceOf[C[Any]].f(a)
Run Code Online (Sandbox Code Playgroud)

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

  b.f(a.asInstanceOf[b.ValueType])
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这个工作,并且在语义上是正确的(即,我们在调用时不"撒谎" asInstanceOf).限制是这有点难以理解.此外,它有点特定于当前情况:如果a,b不是来自相同的迭代器,那么我们在哪里可以应用此类型转换?(此代码还具有对Intelli/J IDEA 2016.2过于复杂的副作用,它在编辑器中将其强调为错误.)

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

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

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

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

  b match {
    case b : C[t] => b.f(a.asInstanceOf[t])
  }
Run Code Online (Sandbox Code Playgroud)

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

sjr*_*jrd 5

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

for ((a, b: C[t]) <- list) {
  b.f(a.asInstanceOf[t])
}
Run Code Online (Sandbox Code Playgroud)

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

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

val (c, d: C[t]) = (a, b)
d.f(c.asInstanceOf[t])
Run Code Online (Sandbox Code Playgroud)

那是因为t在第二行不再是范围了.在这种情况下,您必须使用完整模式匹配.