I've tried my best to understand how the lowerbound works related in type parameterization, but cannot understand it clearly.
Here, some example code for lowerbound.
class Queue[+T] (
private val leading: List[T],
private val trailing: List[T]
) {
private def mirror = {
if (leading.isEmpty){
new Queue(trailing.reverse, Nil)
}
else
this
}
def head = mirror.leading.head
def tail = new Queue(mirror.leading.tail,
mirror.trailing)
/* ====Wrong definition for enqueue====
* def enqueue (item: T) =
* new Queue(leading, item :: trailing)
*/
def enqueue[U >: T] (item: U) =
new Queue[U](leading, item :: trailing)
}
class Fruit (val name: String) {
override def toString = name
}
class Orange extends Fruit("orange")
class Apple extends Fruit("apple")
val queueOrange: Queue[Orange] = new Queue[Orange](Nil, Nil)
queueOrange.enqueue(new Apple())
queueOrange.enqueue(new Orange())
queueOrange.enqueue(new Fruit("PineApple"))
val queueFruit: Queue[Fruit] = queueOrange
queueFruit.enqueue(new Orange())
queueFruit.enqueue(new Apple())
queueFruit.enqueue(new Fruit("Pineapple"))
And here is a result of the execution of the above code.
I have some questions related to above code and results.
I couldn't understand how the code queueOrange.enqueue(new Apple()) can be executed successfully. As far as I know, lowerbound restrict that the type U in the enqueue method should be a supertype of T, which is a lowerbound. However, here Apple and Orange are siblings which extends the same superclass Fruit.
And I couldn't understand how the queueOrange.enqueue(new Apple()) returns the Queue[Fruit] even we feed the new Orange() as enqueue's parameter. I think it could be possible because Orange inherits Fruit, but I couldn't understand how the overall mechanism works that allow question1 and question2 happen.
I couldn't understand why the result different when I used the queueOrange and queueFruit to do same things in the above code.