为什么/这个scala逆变器示例如何工作?

时间:2017-05-29 01:50:39

标签: scala

这段代码似乎设置了一个逆变的例子:FruitBox可以使用苹果或橘子。

class Fruit(name: String) { }
case class Apple(name: String) extends Fruit(name) {}
case class Orange(name: String) extends Fruit(name) {}

class Box[-T] {
  type U >: T
  def put(t: U): Unit= {box += t}
  val box = scala.collection.mutable.ListBuffer[U]()
}

  object test {
   val FruitBox = new Box[Fruit]
   // Fruit Box takes in everything
   FruitBox.put(new Fruit("f1"))
   FruitBox.put(new Orange("o1"))
   FruitBox.put(new Apple("a1")) 

   // and Orange Box takes in only oranges
    val OrangeBox = new Box[Orange]
     //OrangeBox.put(new Apple("o2")  ==> Compile Error that makes sense
    OrangeBox.put(new Orange("o2"))

   // Contra-variant nature is also demonstrated by
     val o: Box[Orange] = FruitBox 
  }

这一切都很好......但为什么有效?特别: 1.当初始化FruitBox时,为什么"键入U>:T"不要将它限制在水果的超类型?尽管存在这种限制,FruitBox能够将子类型放入水果(橙子和苹果)......怎么样?

2 个答案:

答案 0 :(得分:3)

首先,虽然Scala允许您编写new Box[Fruit],但U是一个抽象成员,但我不明白为什么。 但是,在这种情况下,Scala似乎假设U = T。由于您的代码从未实现U,因此可以将其替换为T。因此,您最终会在def put(t: Fruit)中使用FruitBox:当然它接受Apple s,因为它们是Fruit s! Scala唯一知道的U T 1}}它是T的超类型;因此UT的子类型,Fruit的每个子类型也是如此。因此FruitBox.put的任何子类型都可以传递给def put(t: U): Unit。因此put(t: T): Unit实际上与U实际相同,除非您在new Box[Fruit] { type U = Object }中实施class Box[T] { def put(t: T): Unit= {box += t} val box = scala.collection.mutable.ListBuffer[T]() }

  可以放苹果和橘子的FruitBox,以及只能放橙子的OrangeBox。这看起来像反变体行为,我很好。

并非所有逆变行为;你将与​​

完全一致
render() {
   return (
       <div className="App">
           <div className="App-header">
              <h2>BAO BAO BAO</h2>
           </div>
           {this.state.result ? this.renderResult() : this.renderTest()}
           <Baoquestion />
           <AnswerChoices />
           <BaoScore />
           <Score />
       </div>      
    );
}


export default App;

答案 1 :(得分:0)

为了补充@ AlexanderRomanov的答案,Scala将U >: T视为存在类型_ >: T。基本上,当您实际指定它时,它会实现为T

val orangeBox = new Box[Orange]

顺便说一句,另一种更常规的方法是在子类中实现U

trait Box[-T]{
  type U >: T
}

class BoxImpl[T] extends Box[T]{
  type U = T
}

因此,如果您使用类型参数而不是类型成员,则代码将类似于:

class Box[T, _ >: T]
val orangeBox = new Box[Orange, Orange]

在您的情况下,Scala会为您找到合适的类型,以便实现&#34;实现&#34;存在的。

这里唯一不一致的是它实际上不允许:

class BoxImpl[-T] extends Box[T]{
  type U = T
}
//error: contravariant type T occurs in invariant position in type T of type U

所以基本上当您实例化Box[Orange]时,它会忽略-T class BoxImpl[T] extends Box[T]中的Box(无论原始-T是否有data.submit();