我发现自己看了一下Scalawags#2录音,然后出现了关于类型擦除的部分,Dick Wall指出反射最终会让你陷入困境。
所以我在考虑一些我经常做的事情(我也看到了Scala Collections的实现)。假设我有一个带有序列化器的系统,将系统作为类型参数:
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
的类型构造函数参数添加方差不是一个选项答案 0 :(得分:2)
<强>简介强>
我对你的例子感到有些困惑,我可能误解了你的问题,我觉得S
和Tx
之间存在某种类型的递归,我没有从你的问题中得到(因为如果没有,S#Tx
可能是任何东西,我不明白anySer
)的问题
暂定答案:
在编译时,对于Ser[T]
的任何实例,都会有一个定义良好的类型参数T
,因为你想在实例化时保存它,你将拥有一个anySer {{1} }对于给定的特定类型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可能会发生这种情况,那么你的课程就属于1或
评论后编辑
如果你的类型确实是不变的,那么通过强制转换的转换会破坏不变性关系。您基本上是强制类型系统执行非自然转换,因为根据您自己对所编写代码的了解,您知道不会发生任何错误。如果是这种情况,则转换是正确的方法:您正在强制使用与编译器可以正式检查的类型不同的类型,并且您正在明确这样做。我甚至会说一个很大的评论,说明为什么你知道这个操作是合法的,编译器无法猜测并最终附加一个漂亮的单元测试来验证“非正式”关系总是成立。
总的来说,我认为应该谨慎使用这种做法。强类型语言的一个好处是编译器执行正式的类型检查,可以帮助您捕获早期错误。如果你有意破坏它,你会放弃这个巨大的好处。