3

on code below.

My expectation is that T must be a of type B or A, so call to lowerBound(new D) should probably not compile (?). Similar experiments with upperbound give me expected typecheck errors.

Thanks for giving the hint.

object varianceCheck {
  class A {
    override def toString = this.getClass.getCanonicalName
  }

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

  def lowerBound[T >: B](param: T) = { param }

  println(lowerBound(new D))                      //> varianceCheck.D
}
Max
  • 1,741
  • 3
  • 23
  • 40

2 Answers2

5

With your implementation you can write:

scala>   def lowerBound[T >: B](param: T) = { param }
lowerBound: [T >: B](param: T)T

scala> lowerBound(new AnyRef {})
res0: AnyRef = $anon$1@2eef224

where AnyRef is a super type of all object/reference types (actually it is an alias for Java Object class). And this is right, T >: B expresses that the type parameter T or the abstract type T refer to a supertype of type B.

You just have a bad example with toString, cause this method has all object types, but if you change it to, let's say on someMethod, your lowerBound won't compile:

<console>:18: error: value someMethod is not a member of type parameter T
       def lowerBound[T >: B](param: T) = { param.someMethod }

If you change this to T <: B, which means that parameter of type T is a subclass of B, than everything is good, cause this param has someMethod method:

def lowerBound[T <: B](param: T) = { param.someMethod }
4lex1v
  • 21,367
  • 6
  • 52
  • 86
  • Alex, thanks for the answer. What really makes me wonder is the fact that I have to *use* the `someMethod` to see the compile time error. However when I run `def upperBound[T <: B](param: T) = { param }; println(upperBound(new A));` compiler barks at me without any calls to methods. This is what remains unclear for me... How the compiletime typesecurity can possibly be ensured here, if `AnyRef` can be eaten, and no assumtions could be made about the type itself?... – Max Oct 07 '13 at 05:30
  • @MaxP. `A` is a super type for `B`, and your are writing that your param can take object of type `T` which is at least `B`. But what if your `B` class has `someMethod`, while `A` its super type hasn't? And if you won't do understand this things deeper call repl with key: `-Xprint:typer`, which will show you what `scalac typer` phase is doing – 4lex1v Oct 07 '13 at 05:37
  • Alex, i've just reread your answer and a bulb has lit :D - you said it - we expect at least `B`, and `D` is a `B`. So got it! Thanks. – Max Oct 07 '13 at 05:50
4

Faced same question as well. Seems compiler doing great job to help us shoot our feets. If you extract result of the lowerBound you may notice that it is of type B

val b: B = lowerBound(new D)
println(b)                      //> varianceCheck.D

And then if you try though to request type D explicitly

lowerBound[D](new D)

you will see the compiler error that you expected:

Error:(12, 21) type arguments [D] do not conform to method lowerBound's type parameter bounds [T >: B] lowerBound[D](new D)

kharole
  • 133
  • 7