Reputation: 6122
Consider the following hierarchy:
class C1
class C2 extends C1
class C3 extends C2
class C4 extends C3
I want to write a function that just accepts types C2
and C3
. For that I thought of the following:
def f [C >: C3 <: C2](c :C) = 0
I'd expect the following behaviour
f(new C1) //doesn't compile, ok
f(new C2) //compiles, ok
f(new C3) //compiles, ok
f(new C4) // !!! Compiles, and it shouldn't
The problem is when calling it with C4
, which I don't want to allow, but the compiler accepts. I understand that C4 <: C2
is correct and that C4
can be seen as a C3
. But when specifying the bound [C >: C3 <: C2]
, I would expect the compiler to find a C
that respects both bounds at the same time, not one by one.
Question is : Is there any way to achieve what I want, and if not, is the compiler trying to avoid some inconsistency with this?
Edit: from the answers I realized that my presumption is wrong. C4
always fulfills C >: C3
, so both bounds are indeed respected. The way to go for my use case is C3 <:< C
.
Upvotes: 4
Views: 704
Reputation: 842
I found that explanation from another stackoverflow question very helpful:
S >: T simply means that if you pass in a type S that is equal to T or its parent, then S will be used. If you pass a type that is sublevel to T then T will be used.
So in your example all, but first should compile. Following example illustrates the meaning of that: Let's redefine f:
def f[U >: C3 <: C2](c: U) = c
and then:
val a2 = f(new C2)
val a3 = f(new C3)
val a4 = f(new C4)
List[C2](a2, a3, a4) //compiles
List[C3](a3, a4) //compiles
List[C4](a4) //does not cause a4 is C3
Hope that helps.
Upvotes: 5
Reputation: 14224
Statically, yes. It's pretty simple to impose this constraint:
def f[C <: C2](c: C)(implicit ev: C3 <:< C) = 0
f(new C4)
wouldn't compile now.
The problem is, it's probably not possible to prohibit the following behaviour at compile time:
val c: C3 = new C4
f(c)
Here variable c
has static type C3
, which passes any kind of typechecking by compiler, but it is actually a C4
at runtime.
At runtime you can of course check the type using reflection or polymorphism and throw errors or return Failure(...)
or None
Upvotes: 11