4

考虑以下层次结构:

class C1
class C2 extends C1
class C3 extends C2
class C4 extends C3

我想编写一个只接受类型C2C3. 为此,我想到了以下几点:

 def f [C >: C3 <: C2](c :C) = 0

我期望以下行为

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 

问题是用 调用它时C4,我不想允许,但编译器接受。我理解这C4 <: C2是正确的,C4可以看作是C3. 但是在指定 bound 时[C >: C3 <: C2],我希望编译器找到一个C同时尊重两个边界的,而不是一个一个。

问题是:有什么方法可以实现我想要的,如果没有,编译器是否试图避免与此不一致?

编辑:从答案中我意识到我的假设是错误的。C4总是满足C >: C3,所以这两个界限确实受到尊重。我的用例的方法是C3 <:< C.

4

2 回答 2

11

静态的,是的。施加此约束非常简单:

def f[C <: C2](c: C)(implicit ev: C3 <:< C) = 0

f(new C4)现在不会编译。

问题是,在编译时可能无法禁止以下行为:

val c: C3 = new C4
f(c)

这里变量c具有静态类型C3,它通过编译器的任何类型检查,但它实际上是C4在运行时。

在运行时,您当然可以使用反射或多态检查类型并抛出错误或返回Failure(...)None

于 2015-07-24T11:55:28.833 回答
5

我发现另一个stackoverflow问题的解释非常有帮助:

S >: T 仅仅意味着如果你传入一个类型 S 等于 T 或其父级,那么 S 将被使用。如果您将子级别的类型传递给 T ,则将使用 T 。

所以在你的例子中,但首先应该编译。下面的例子说明了它的含义:让我们重新定义 f:

def f[U >: C3 <: C2](c: U) = c

接着:

 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

希望有帮助。

于 2015-07-24T11:45:55.647 回答