class A
class B extends A
class C extends B
class D extends C

class E

class X[+T] { // T covariant for input types
def add[U >: T](x: U): X[U] = this // U contravariant for return type
}

class Y[+T <: A] { // This time with an upper bound A
def add[U >: T <: A](y: U): Y[U] = this
}

val a = new A
val b = new B
val c = new C
val d = new D

val x = new X[C]
val x2 = x.add(b) // x2 type becomes X[B]
val x3 = x.add(d) // x3 remains of type X[C]
val x4 = x.add(10) // x4 type becomes X[Any]
val x6 = x.add("Z") // x5 type becomes X[java.lang.Object]

val y = new Y[C]
val y2 = y.add(b) // y2 type becomes X[B]
val y3 = y.add(d) // y3 remains of type X[C]
//val y4 = y.add(2) // Won't compile as Y has an upper bound, it must inherits from A


class Z {
def add[T<%E](e:T) = this // View Bound, it exists an implicit conversion to E
}
val z = new Z
//val z1 = z.add(a) // Won't compile as there no implicit conversion from A to E
implicit def a2E(a:A) = new E
val z2 = z.add(a)