我发现自己在观看 Scalawags#2 的一段录音,然后出现了关于类型删除的部分,Dick Wall 指出反射最终会咬住你的脚。
所以我在思考我经常做的事情(我也看到了 Scala 集合的实现)。假设我有一个带有序列化器的系统,该序列化器将系统作为类型参数:
trait Sys[S <: Sys[S]] { type Tx }
trait FooSys extends Sys[FooSys]
trait Serializer[S <: Sys[S], A] {
def read(implicit tx: S#Tx): A
}
现在有许多类型A
可以在没有值参数的情况下构建序列化器,因此本质上系统类型参数是“空的”。由于在我的示例中大量调用了序列化程序,因此我正在保存实例化:
object Test {
def serializer[S <: Sys[S]] : Serializer[S, Test[S]] =
anySer.asInstanceOf[Ser[S]]
private val anySer = new Ser[FooSys]
private final class Ser[S <: Sys[S]] extends Serializer[S, Test[S]] {
def read(implicit tx: S#Tx) = new Test[S] {} // (shortened for the example)
}
}
trait Test[S <: Sys[S]]
我知道这是正确的,但是,asInstanceOf
当然有难闻的气味。对于这种方法有什么建议吗?让我添加两件事
- 将类型参数从特征
Serializer
的构造函数移动到read
方法不是一个选项(有特定的序列化器需要在S中参数化值参数
) - 不可以向
Serializer
的类型构造函数参数添加差异
最佳答案
简介:
我对你的例子有点困惑,我可能误解了你的问题,我有一种感觉 S
之间存在某种类型的递归。和Tx
我没有从你的问题中得到答案(因为如果不是, S#Tx
可能是任何东西,我不明白 anySer
的问题)
暂定答案:
在编译时,对于 Ser[T]
的任何实例将有一个明确定义的类型参数 T
,因为您想在实例化时保存它,所以您将有一个 anySer Ser[T]
对于给定的特定类型 A
您在某种程度上所说的是 Ser[A]
将作为 Ser[S]
对于任何 S
。根据类型A和S之间的关系,这可以用两种方式解释。
如果每个
A<:<S
都可以进行此转换那么你的序列化器是COVARIANT
你可以将你的anySer初始化为Ser[Nothing]
。由于 Nothing 是 Scala 中每个类的子类,因此您的 anySer 将始终以Ser[Whatever]
的方式工作。如果每个
S<:<A
都可以进行此转换那么你的序列化器是CONTRAVARIANT
你可以将你的anySer初始化为Ser[Any]
。由于 Any 是 Scala 中每个类的子类,因此您的 anySer 将始终作为Ser[Whatever]
工作。如果不是前一种情况,则意味着:
def serializer[S <: Sys[S]] : Serializer[S, Test[S]] = anySer.asInstanceOf[Ser[S]]
可能会在运行时产生可怕的失败,因为有一些 S 序列化器无法工作。如果不存在可能发生这种情况的 S,那么您的类(class)属于 1 或
评论后编辑
如果您的类型确实是不变的,则通过强制转换进行的转换会破坏不变关系。您基本上是在强制类型系统执行不自然的转换,因为根据您对所编写代码的了解,您知道不会发生任何错误。如果是这种情况,那么强制转换是正确的方法:您正在强制使用与编译器可以正式检查的类型不同的类型,并且您正在明确这一点。我什至会写一个大评论说为什么你知道操作是合法的并且编译器无法猜测并最终附加一个漂亮的单元测试来验证“非正式”关系始终成立。
总的来说,我认为这种做法应该非常小心地使用。强类型语言的好处之一是编译器执行正式的类型检查,帮助您捕获早期错误。如果你故意破坏它,你就放弃了这个大好处。
关于scala - 转换 "hollow"更高种类的类型值以避免实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14397548/