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.