猿问

为什么这个示例不编译,也就是(协、对、内)方差是如何工作的?

为什么这个示例不编译,也就是(协、对、内)方差是如何工作的?

这个问题,有人能用Scala解释一下以下情况吗?

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

我明白+TT在类型声明中(如果我使用T)。但是,如何编写一个类,该类在其类型参数中是协变的,而不诉诸于创建非参数化?如何确保只能使用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) { }


翻过高山走不出你
浏览 425回答 3
3回答

慕勒3428872

一般说来,协变类型参数允许在类是子类型时向下变化(或者,随子类型而变化,因此是“co-”前缀)。更具体而言:trait&nbsp;List[+A]List[Int]是List[AnyVal]因为Int是AnyVal..这意味着您可以提供List[Int]当类型值为List[AnyVal]都是意料之中的。对于泛型来说,这确实是一种非常直观的工作方式,但事实证明,在存在可变数据的情况下使用它是不健全的(破坏了类型系统)。这就是为什么泛型在Java中是不变的。使用Java数组(这些数组是错误的协变量)的不稳定的简单示例:Object[]&nbsp;arr&nbsp;=&nbsp;new&nbsp;Integer[1];arr[0]&nbsp;=&nbsp;"Hello,&nbsp;there!";我们刚刚分配了一个类型的值String类型数组Integer[]..出于显而易见的原因,这是个坏消息。Java的类型系统实际上允许在编译时这样做。JVM将“帮助”抛出ArrayStoreException在运行时。Scala的类型系统防止了此问题,因为Array类是不变的(声明是[A]而不是[+A]).请注意,还有另一种类型的差异称为反向方差..这是非常重要的,因为它解释了为什么协方差会引起一些问题。反方差实际上是协方差的对立面:参数是可变的。向上有亚型。虽然它确实有一个非常重要的应用程序:函数,但它并不常见,部分原因是它违反了直觉。trait&nbsp;Function1[-P,&nbsp;+R]&nbsp;{ &nbsp;&nbsp;def&nbsp;apply(p:&nbsp;P):&nbsp;R}注意“-“对P类型参数这个声明作为一个整体意味着Function1是逆变的P和协变R..因此,我们可以导出以下公理:T1'&nbsp;<:&nbsp;T1 T2&nbsp;<:&nbsp;T2'----------------------------------------&nbsp;S-FunFunction1[T1,&nbsp;T2]&nbsp;<:&nbsp;Function1[T1',&nbsp;T2']注意T1'的子类型(或同一类型)。T1,而相反的是T2和T2'..英文本可阅读如下:函数A是另一个函数的子类型。B如果参数类型为A的参数类型的超级类型。B的返回类型A的返回类型的子类型。B.这个规则的原因是留给读者练习的(提示:考虑不同的情况,因为函数是子类型的,就像上面我的数组示例)。使用您新发现的协方差和反向方差知识,您应该能够了解为什么下面的示例将不编译:trait&nbsp;List[+A]&nbsp;{ &nbsp;&nbsp;def&nbsp;cons(hd:&nbsp;A):&nbsp;List[A]}问题是A是协变的,而cons函数的类型参数为不变量..因此,A改变了错误的方向。有趣的是,我们可以通过List逆变A,但是返回类型List[A]将无效,因为cons函数的返回类型为协变.我们这里只有两个选择A不变量,丢失协方差的良好、直观的子类型属性,或(B)向cons方法,该方法定义A作为下限:def&nbsp;cons[B&nbsp;>:&nbsp;A](v:&nbsp;B):&nbsp;List[B]现在这是有效的。你可以想象A是向下变化的,但是B能够向上变化A自A是它的下界。通过这个方法声明,我们可以A是协变的,一切都成功了。注意,只有当我们返回List,它专门针对不太特定的类型。B..如果你想List可变的,因为你最终试图分配类型的值,事情就会变坏。B类型变量A,这是编译器不允许的。无论何时有可变性,都需要有某种类型的变体,这需要某种类型的方法参数,这意味着(与访问器一起)意味着不变性。协方差适用于不可变数据,因为唯一可能的操作是访问器,访问器可以被赋予协变量返回类型。

白猪掌柜的

简单地说,如果允许的话:&nbsp;&nbsp;class&nbsp;Slot[+T](var&nbsp;some:&nbsp;T)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;get:&nbsp;T&nbsp;=&nbsp;some&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;} &nbsp;&nbsp;val&nbsp;slot:&nbsp;Slot[Dog]&nbsp;=&nbsp;new&nbsp;Slot[Dog](new&nbsp;Dog)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;val&nbsp;slot2:&nbsp;Slot[Animal]&nbsp;=&nbsp;slot&nbsp;&nbsp;//because&nbsp;of&nbsp;co-variance&nbsp; &nbsp;&nbsp;slot2.some&nbsp;=&nbsp;new&nbsp;Animal&nbsp;&nbsp;&nbsp;//legal&nbsp;as&nbsp;some&nbsp;is&nbsp;a&nbsp;var &nbsp;&nbsp;slot.get&nbsp;??slot.get将在运行时抛出一个错误,因为它在转换Animal到Dog(哼!)一般说来,变异与协方差和反向方差不太一致。这就是为什么所有Java集合都是不变的原因。
随时随地看视频慕课网APP
我要回答