考虑下面的两个代码。他们完成相同的目标:只有这样A[T]
-s可以存储在T
延伸C
斯卡拉存在与协方差
但是他们使用两种不同的方法来实现这一目标的Container
:
1)existentials
2)协方差
我更喜欢第一种解决方案,因为A
保持简单。为什么我会想要使用第二种解决方案(协方差),有什么理由吗?
我与第二个解决方案的问题是,它是在这个意义上,它不应该是A
-s责任来形容我可以在一个容器仓库并没有什么不自然,这应该是容器的责任。第二种解决方案也是更复杂一旦我想开始在A
上运行,然后我必须处理所有与协变有关的东西。
使用第二种(更复杂,不太自然)的解决方案会带来什么好处?
object Existentials extends App {
class A[T](var t:T)
class C
class C1 extends C
class C2 extends C
class Z
class Container[T]{
var t:T = _
}
val c=new Container[A[_<:C]]()
c.t=new A(new C)
// c.t=new Z // not compile
val r: A[_ <: C] = c.t
println(r)
}
object Cov extends App{
class A[+T](val t:T)
class C
class C1 extends C
class C2 extends C
class Z
class Container[T]{
var t:T = _
}
val c: Container[A[C]] =new Container[A[C]]()
c.t=new A(new C)
//c.t=new A(new Z) // not compile
val r: A[C] = c.t
println(r)
}
EDIT(响应阿列克谢的答案):
评论: “我与第二个解决方案的问题是,它不是在某种意义上说,它不应该是作为责任自然描述我可以在集装箱中储存什么,哪些不是,这应该是集装箱的责任。“
如果非要class A[T](var t:T)
这意味着,我可以仅存储A[T]
在容器-s和不(A[S]
其中S<:T
),在任何容器。
但是,如果我有class A[+T](var t:T)
那么我可以存储A[S]
哪里S<:T
以及任何容器。
因此,当声明A
或者是不变的或协变时,我决定哪种类型的A [S]可以存储在一个容器中(如上所示),这个决定发生在声明A
。
不过,我认为,这个决定应该发生,相反,在容器的声明,因为它是具体的东西将被允许进入该容器中,只有A[T]
-s或也A[S]
其中S<:T
-s容器。
换句话说,改变A[T]
方差具有全球影响,同时改变一个容器的类型参数从A[T]
到A[_<:S]
已经容器本身上的良好限定的局部作用。所以“变化应该有局部效应”的原则也有利于生存解决方案。
感谢您的回答。 “另外,如果你忘记在一种方法中使用正确的存在,任何其他调用它的地方都不能使用存在类型(没有不安全的未经检查的转换)。”你能举一个简单的例子吗?我不完全明白你的意思。 – jhegedus
“在第一种情况下,A更简单,但在第二种情况下,其客户是这样。”客户如何更简单?我不明白,你能举个例子吗? – jhegedus
@jhegedus查看编辑。 –