简体   繁体   中英

Scala type bounds behaviour (method vs class)

Probably an easy one for Scala gurus. I'm reading up on type bound constraints and wondering if I'm missing something, as I'm seeing a slightly unexpected behaviour. Let's say we have three types A,B,C in a hierarchy like this, for which we're experimenting with type bounds:

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]
  }

}

The collections are behaving as expected for all except one case, see l1.add(new C); not causing a compilation error. Why is this allowed?

Thanks!

I'm not claiming to be a scala guru, but I'll take a stab at this.

You've defined your type hierarchy along the lines of A >: B >: C , so you can do stuff like

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

just like you could say val a: Any = new Whatever .

So when you try to add a new C to l1 , it gets treated like a B instance because that is the closest available version of itself that conforms to the method's type bounds.

Lower bounds are not useful when used in that way. You cannot enforce a value parameter to have a lower bound because, according to the Liskov substitution principle , you must be able to use an instance of a subclass anywhere an instance of a class is required. Therefore, your code compiles because new C can be seen as an instance of B .

Generally, it is not useful to put a lower bound on a value parameter of a method because of that reason. Lower bounds are definitely more useful in your second case, where you bound the type parameter of a class. All type parameters are resolved at compile type and, as such, you can expect the compiler to yield compiler errors in your second case. You can find a more concrete example of lower bound usage here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM