Scala类型边界行为(方法与类)

时间:2012-12-23 13:58:24

标签: scala generics types bounds

对于Scala大师来说可能很容易。我正在阅读关于类型限制的约束,并想知道我是否遗漏了某些东西,因为我看到了一个略微出乎意料的行为。假设我们在这样的层次结构中有三种类型A,B,C,我们正在尝试使用类型边界:

class A {
}

class B extends A {
}

class C extends B {
}

class MyList1 { // T super B
  def add[T >: B](a: T): Unit = {}
}

class MyList2 { // T extends B
  def add[T <: B](a: T): Unit = {}
}

class MyList3[T >: B] { // T super B
  def add(a: T): Unit = {}
}

class MyList4[T <: B] { // T extends B
  def add(a: T): Unit = {}
}

object BoundsClass {

  def main(args: Array[String]) {
    val l1 = new MyList1
    l1.add(new A) 
    l1.add(new B)
    l1.add(new C) // why is this allowed??

    val l2 = new MyList2
    // l2.add(new A) // not allowed (OK)
    l2.add(new B)
    l2.add(new C)

    val l3a = new MyList3[A]
    val l3b = new MyList3[B]
    // val l3c = new MyList3[C] // not allowed (OK)

    // val l4a = new MyList4[A] // not allowed (OK)
    val l4b = new MyList4[B]
    val l4c = new MyList4[C]
  }

}

除了一种情况外,集合的行为符合预期,参见l1.add(new C);不会导致编译错误。为什么允许这样做?

谢谢!

2 个答案:

答案 0 :(得分:3)

我并不是自称是斯卡拉大师,但我会抨击它。

您已经在A >: B >: C的行中定义了类型层次结构,因此您可以执行类似

的操作
val aa: A = new A
val ab: A = new B
val ac: A = new C

val bb: B = new B
val bc: B = new C

val cc: C = new C

就像你可以说val a: Any = new Whatever

因此,当您尝试将new C添加到l1时,它会被视为B实例,因为它是符合方法类型边界的最接近的可用版本。

答案 1 :(得分:0)

以这种方式使用时,下限无效。您不能强制使用值参数来设置下限,因为根据Liskov substitution principle,您必须能够在需要类的实例的任何地方使用子类的实例。因此,您的代码会进行编译,因为new C可以被视为B的实例。

通常,由于这个原因,在方法的值参数上设置下限是没有用的。在第二种情况下,下限肯定更有用,您可以在其中绑定类的类型参数。所有类型参数都在编译类型中解析,因此,您可以期望编译器在第二种情况下产生编译器错误。您可以找到下限使用here的更具体示例。