这个问题时,有人可以解释以下Scala中:

class Slot[+T] (var some: T) { 
   //  DOES NOT COMPILE 
   //  "COVARIANT parameter in CONTRAVARIANT position"

}

我明白之间的区别的 +T 和<强> T 在类型声明(它编译,如果我使用 T )。但随后一个人如何实际编写这是在其类型参数的协变的一类,而不诉诸创造的东西的 unparametrized 的?我怎样才能确保下面只能通过实例被创建的 T

class Slot[+T] (var some: Object){    
  def get() = { some.asInstanceOf[T] }
}

修改 - 现在得到这个下降到以下内容:

abstract class _Slot[+T, V <: T] (var some: V) {
    def getT() = { some }
}

这是一切都很好,但我现在有两个类型参数,在这里我只想要一个。我会因此重新提出这样的问题:

<强>如何写一个不可变 Slot类,这是协变在其类型吗

修改2 :咄!我用var而不是val。下面是我想要的东西:

class Slot[+T] (val some: T) { 
}
有帮助吗?

解决方案

一般地,一协变型参数是一个被允许作为类亚型改变向下(或者,变化与子类型,因而有“共”前缀)。更具体地:

trait List[+A]

List[Int]List[AnyVal]的子类型,因为IntAnyVal的子类型。这意味着,你可以提供List[Int]的实例时,预计类型List[AnyVal]的值。这实在是对仿制药的工作非常直观的方式,但事实证明,这是不合理的(打破类型系统)的可变数据的情况下使用时。这就是为什么泛型是Java不变。使用Java阵列不健全的简单的例子(其是错误地协变):

Object[] arr = new Integer[1];
arr[0] = "Hello, there!";

我们刚分配类型String的值以式Integer[]的阵列。对于这应该是显而易见的原因,这是个坏消息。 Java的类型系统实际上允许这种在编译时。 JVM将“很有帮助”在运行时抛出ArrayStoreException。因为在Array类类型参数是不变的(声明[A]而非[+A])Scala的类型系统防止了这种问题。

请注意,有被称为逆变方差的另一种类型。这是非常重要的,因为它解释了为什么协方差可能会导致一些问题。逆变是字面上协方差的相反:参数变化的向上与子类型。它是少了很多共同部分,因为它是如此反直觉,虽然它确实有一个非常重要的应用:功能

trait Function1[-P, +R] {
  def apply(p: P): R
}

请注意 “的 - ” 方差对P类型参数注释。该声明作为一个整体意味着Function1是在P R和协变逆变。因此,我们可以得出以下公理:

T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']

注意,T1'必须T1的子类型(或相同类型),而它是相对来说T2T2'。在英语中,这可以被理解为如下:

  

一个函数的 A 的是另一种功能的的一个子类型,如果的参数类型的 A 的是参数类型的乙而返回类型 A 的是的返回类型的子类型的

这样做的原因规则被留给读者作为练习给读者。(提示:考虑不同的情况下,作为功能亚型,如我的阵列例如从上方)

使用你的新发现合作和逆变的知识,你应该能够明白为什么下面的例子将不能编译:

trait List[+A] {
  def cons(hd: A): List[A]
}

的问题是,A是协变,而cons功能预计其类型参数是不变。因此,A是变化的方向错了。有趣的是,我们可以通过使List逆变在A解决这个问题,但随后的返回类型List[A]将是无效的,因为cons功能预计其返回类型为的。

我们的只有两个选择这里是a)作出A不变,失去协方差的美观大方,直观的子分型性,或者b)一个本地类型参数添加到其中cons定义为下界A方法:

def cons[B >: A](v: B): List[B]

这是现在有效。你可以想像,A向下不同,但B能够相对于向上变化到A因为A是其下限。使用这种方法的声明,我们可以有A是协变,一切工作了。

请注意,THI把戏,只有当我们回到这是专门对无特定类型List B的实例作品。如果你试图让List可变的,东西打破,因为你最终要类型B的值赋给类型A,这是由编译器不允许的变量。只要你有可变性,你需要有某种形式的,这需要某种类型,这(与存取一起)意味着不变性的方法参数的赋值函数。协方差可与不可变的数据,因为唯一可能的操作是一个访问器,其可被给定协变返回类型。

其他提示

@Daniel解释很好。但解释它在短,如果它被允许:

  class Slot[+T](var some: T) {
    def get: T = some   
  }

  val slot: Slot[Dog] = new Slot[Dog](new Dog)   
  val slot2: Slot[Animal] = slot  //because of co-variance 
  slot2.some = new Animal   //legal as some is a var
  slot.get ??
然后

slot.get将在运行时引发错误,因为它是在一个转换到Animal Dog(杜!)不成功的。

在一般的可变性不具有共方差和反方差顺利。这就是为什么所有的Java集合是不变的原因。

您需要申请一个下界参数。我有一个很难记住语法,但我认为这会是这个样子:

class Slot[+T, V <: T](var some: V) {
  //blah
}

在Scala的按示例是有点难以理解,几个具体的例子会帮助。

scroll top