用WeakTypeTag
s:
import reflect.runtime.universe.WeakTypeTag
import reflect.runtime.universe.weakTypeOf
package object stackoverflow1 {
sealed trait Status
trait Open extends Status
trait Closed extends Status
case class Buffer[S <: Status](available: Int)
object Buffer {
def create(available: Int): Buffer[Open] = new Buffer[Open](available)
def from[S <: Status: WeakTypeTag](b: Buffer[S]): Buffer[Open] = {
weakTypeOf[S] match {
case t if t <:< weakTypeOf[Open] => b.asInstanceOf[Buffer[Open]]
case t if t <:< weakTypeOf[Closed] => new Buffer[Open](b.available)
}
}
}
}
package stackoverflow1 {
object Main extends App {
val b1 = Buffer.create(1024)
val b2 = Buffer.from(b1)
}
}
这很好用,但是当我将包对象的代码移动到特征中并将该特征混合到包对象中时,它不再起作用了:
import reflect.runtime.universe.WeakTypeTag
import reflect.runtime.universe.weakTypeOf
package stackoverflow2 {
trait Mixin {
sealed trait Status
trait Open extends Status
trait Closed extends Status
case class Buffer[S <: Status](available: Int)
object Buffer {
def create(available: Int): Buffer[Open] = new Buffer[Open](available)
def from[S <: Status: WeakTypeTag](b: Buffer[S]): Buffer[Open] = {
weakTypeOf[S] match {
case t if t <:< weakTypeOf[Open] => b.asInstanceOf[Buffer[Open]]
case t if t <:< weakTypeOf[Closed] => new Buffer[Open](b.available)
}
}
}
}
}
package object stackoverflow2 extends Mixin
package stackoverflow2 {
object Main extends App {
val b1 = Buffer.create(1024)
val b2 = Buffer.from(b1)
}
}
的ErrorMessage:
Exception in thread "main" scala.MatchError: stackoverflow2.Open (of class scala.reflect.internal.Types$ClassNoArgsTypeRef)?
一些想法为什么?
编辑
使用抽象类型成员而不是类型参数:
import reflect.runtime.universe._
package stackoverflow3 {
trait Mixin {
sealed trait Status
trait Open extends Status
trait Closed extends Status
case class Buffer(available: Int) {
type S <: Status
def withStatus[S <: Status] = asInstanceOf[BufferAux[S]]
}
type BufferAux[S0 <: Status] = Buffer { type S = S0 }
object Buffer {
def create(available: Int): BufferAux[Open] = Buffer(available).withStatus[Open]
def from[S <: Status: WeakTypeTag](b: BufferAux[S]): BufferAux[Open] = {
weakTypeOf[S] match {
case t if t <:< weakTypeOf[Open] => b.asInstanceOf[BufferAux[Open]]
case t if t <:< weakTypeOf[Closed] => Buffer(b.available).withStatus[Open] // no need for creating new Buffer, only shows intend
}
}
}
}
}
package object stackoverflow3 extends Mixin
package stackoverflow3 {
object Main extends App {
val b1 = Buffer.create(1024)
val b2 = Buffer.from(b1)
}
}
答案 0 :(得分:3)
让我们为Buffer.from
方法添加一些调试打印:
import scala.reflect.runtime.universe.showRaw
println(s"scrut = ${weakTypeOf[S]}, ${showRaw(weakTypeOf[S])}")
println(s"Open = ${weakTypeOf[Open]}, ${showRaw(weakTypeOf[Open])}")
println(s"Closed = ${weakTypeOf[Closed]}, ${showRaw(weakTypeOf[Closed])}")
这将打印出有问题的类型及其结构。也许这可以解释发生了什么:
scrut = stackoverflow2.Open, TypeRef(SingleType(ThisType(stackoverflow2), stackoverflow2.package), TypeName("Open"), List())
Open = Mixin.this.Open, TypeRef(ThisType(stackoverflow2.Mixin), TypeName("Open"), List())
Closed = Mixin.this.Closed, TypeRef(ThisType(stackoverflow2.Mixin), TypeName("Closed"), List())
好吧,这显示了相关类型的完全限定版本,并揭示了可能存在问题的差异。因此,反思说stackoverflow2.Open
不是Mixin.this.Open
的子类型。嗯,但包对象stackoverflow2
扩展Mixin
。这一定是反思中的错误,对吧?
让我们问问第三方并检查scalac对此有何评论。我们在val test: Mixin.this.Open = (??? : stackoverflow2.Open)
内编写类似Buffer.from
的内容并尝试编译?
Test.scala:17: error: type mismatch;
found : stackoverflow2.Open
required: Mixin.this.Open
val test: Open = (??? : stackoverflow2.Open)
^
one error found
好吧,scalac实际上同意反思,所以也许这不是一个错误。它确实不是错误,因为如果我们创建另一个Mixin
的子类,那么它将拥有自己的Mixin.this.Open
副本,这将与stackoverflow2
的{{1}}副本不兼容{1}}。这意味着Mixin.this.Open
理所当然不是stackoverflow2.Open
的子类型。
我们在这里有几个选择。
首先,我们可以写Mixin.this.Open
而不是t <:< weakTypeOf[Open]
。不漂亮,因为这会将有关子类的知识放入超类中。
其次,我们可以使用t <:< weakTypeOf[stackoverflow2.Open]
上的asSeenFrom
将weakTypeOf[Open]
调整为传递给Mixin.this.Open
的类型的容器。这将安抚子类型检查器,因为它会将一棵树上的苹果与同一棵树上的苹果进行比较,可以这么说:
from
第三,我们可以进行手动检查以验证:a)我们传递的类型是在type InternalType = scala.reflect.internal.SymbolTable#Type
type ApiType = Type
val pre = weakTypeOf[S].asInstanceOf[InternalType].prefix.asInstanceOf[ApiType]
val topen = weakTypeOf[Open].asSeenFrom(pre, symbolOf[Mixin])
...
case t if t <:< topen => b.asInstanceOf[Buffer[Open]]
...
的子类型中声明的,b)基础符号的名称是Mixin
或者Open
。这似乎比第二个选项更容易,但实际上我不建议去做,因为当你自己重新实现这样的东西时,很容易忽略一些角落的情况。例如,上面提供的配方实际上是错误的,因为除了a)和b)之外,我们还需要检查c)底层符号是从Closed
继承而不是重新定义(即阴影)。我可能会忽略其他一些东西,所以第三种选择绝对是风险最大的选择。