Reputation: 9114
I have this scala code:
class Creature {
override def toString = "I exist"
}
class Person(val name: String) extends Creature {
override def toString = name
}
class Employee(override val name: String) extends Person(name) {
override def toString = name
}
class Test[T](val x: T = null) {
def upperBound[U <: T](v: U): Test[U] = {
new Test[U](v)
}
def lowerBound[U >: T](v: U): Test[U] = {
new Test[U](v)
}
}
We can see the hierarchy relationship between Creature, Person, and Employee:
Creature <- Person <- Employee
In the def main:
val test = new Test[Person]()
val ub = test.upperBound(new Employee("John Derp")) //#1 ok because Employee is subtype of Person
val lb = test.lowerBound(new Creature()) //#2 ok because Creature is supertype of Person
val ub2 = test.upperBound(new Creature()) //#3 error because Creature is not subtype of Person
val lb2 = test.lowerBound(new Employee("Scala Jo")) //#4 ok? how could? as Employee is not supertype of Person
What can I understand is:
A <: B
define A must be subtype or equal to B (upper bound)
A >: B
define A must be supertype or equal to B (lower bound)
But what happened to #4 ? Why there is no error? As the Employee is not supertype of Person, I expect it shouldn't conform to the bound type parameter [U >: T]
.
Anyone can explain?
Upvotes: 1
Views: 599
Reputation: 22374
This example may help
scala> test.lowerBound(new Employee("Scala Jo"))
res9: Test[Person] = Test@1ba319a7
scala> test.lowerBound[Employee](new Employee("Scala Jo"))
<console>:21: error: type arguments [Employee] do not conform to method lowerBound's type parameter bounds [U >: Person]
test.lowerBound[Employee](new Employee("Scala Jo"))
^
In general, it's connected to the Liskov Substitution Principle - you can use subtype anywhere instead of supertype (or "subtype can always be casted to its supertype"), so type inference trying to infer as nearest supertype as it can (like Person
here).
So, for ub2 there is no such intersection between [Nothing..Person]
and [Creature..Any]
, but for lb2 there is one between [Person..Any]
and [Employee..Any]
- and that's the Person
. So, you should specify type explicitly (force Employee
instead of [Employee..Any]
) to avoid type inference here.
The example where lb2 expectedly failing even with type inference:
scala> def aaa[T, A >: T](a: A)(t: T, a2: A) = t
aaa: [T, A >: T](a: A)(t: T, a2: A)T
scala> aaa(new Employee(""))(new Person(""), new Employee(""))
<console>:19: error: type arguments [Person,Employee] do not conform to method aaa's type parameter bounds [T,A >: T]
aaa(new Employee(""))(new Person(""), new Employee(""))
^
Here type A
is inferred inside first parameter list and fixated as Employee
, so second parameter list (which throws an error) just have only choice - to use it as is.
Or most commonly used example with invariant O[T]
:
scala> case class O[T](a: T)
defined class O
scala> def aaa[T, A >: T](t: T, a2: O[A]) = t
aaa: [T, A >: T](t: T, a2: O[A])T
scala> aaa(new Person(""), O(new Employee("")))
<console>:21: error: type mismatch;
found : O[Employee]
required: O[Person]
Note: Employee <: Person, but class O is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
aaa(new Person(""), O(new Employee("")))
^
T
is fixed to Employee
here and it's not possible to cast O[Employee]
to
O[Person]
due to invariance by default.
Upvotes: 2
Reputation: 1512
I think it happens because you can pass any Person
to your
Test[Person].lowerBound(Person)
and as Employee
is a subclass of Person
it is considered as Person
which is legal here.
Upvotes: 1