使用泛型参数的Scala方法调用看起来不是多态的 - 出了什么问题

时间:2012-12-07 05:12:57

标签: scala scala-2.9

无法弄清楚为什么这是错误的或如何解决它。这是重现问题的“蒸馏”代码。请帮助,但我不会理解“为什么”这些问题 - 有非常真实有效的答案,但它们是专有且不可更改的,因此与解决方案无关。

object Sandbox {

  // --- Following are really Java interfaces/classes ---
  trait R[X <: U[X,Y], Y <: E[X,Y]];
  class U[X <: U[X,Y], Y <: E[X,Y]] extends R[X,Y];
  class E[X <: U[X,Y], Y <: E[X,Y]] extends R[X,Y];

  trait R2 extends R[U2,E2];
  class U2 extends U[U2,E2] with R2;
  class E2 extends E[U2,E2] with R2;
  // --- End Java interfaces/classes ---

  def trouble[X <: U[X,Y], Y <: E[X,Y], Z <: R[X,Y]](r: Z) {}

  def main(args: Array[String]) {
    trouble(new U());  // Fine
    trouble(new E());  // Fine
    trouble(new U2()); // Not fine, reports:
    /*
     * inferred type arguments [Nothing,Nothing,Sandbox.U2]
     * do not conform to method trouble's type parameter bounds
     * [X <: Sandbox.U[X,Y],Y <: Sandbox.E[X,Y],Z <: Sandbox.R[X,Y]]
     */


    trouble(new E2()); // Not fine, reports:
    /*
     * inferred type arguments [Nothing,Nothing,Sandbox.E2]
     * do not conform to method trouble's type parameter bounds
     * [X <: Sandbox.U[X,Y],Y <: Sandbox.E[X,Y],Z <: Sandbox.R[X,Y]]
     */

    trouble[U2,E2,R2](new U2()); // Fine
    trouble[U2,E2,R2](new E2()); // Fine
  }
}

编译器似乎无法根据指定的单个参数推断出“麻烦”方法的X,Y和Z类型args。我明白了 - 当我指定类型时,它没关系,但它非常麻烦。有没有办法以某种方式轻推/帮助编译器,这样就不会出现问题?

也许我对Scala的类型推理系统过于自信,但所有信息都可以使用。

提前致谢!

2 个答案:

答案 0 :(得分:4)

您对Scala的类型系统推断过于自信。您尝试使用这些更复杂(尤其是递归)类型定义的次数越多,您就会发现它越多。我不知道我是否可以提供“为什么不能解决这个问题”,但我可以提供一些有用的东西:

不要在类型上参数化R,但要使它们成为必须以子类型声明的抽象成员:

trait R {
    type X <: U[X,Y]
    type Y <: E[X,Y]
}

class U[X0 <: U[X0,Y0],Y0 <: E[X0,Y0]] extends R {
    type X = X0
    type Y = Y0
}
class E[X0 <: U[X0,Y0], Y0 <: E[X0,Y0]] extends R {
    type X = X0
    type Y = Y0
}

trait R2 extends R;
class U2 extends U[U2,E2] with R2
class E2 extends E[U2,E2] with R2

def trouble[X <: U[X,Y], Y <: E[X,Y], Z <: R](r: Z) {}

然后我相信你会发现你的主要方法编译没有改变。

另外,代码中的每个分号都可以在不改变含义的情况下删除。

答案 1 :(得分:1)

请参阅this answer(以及我在那里链接的答案),讨论Scala的类型推断的局限性,这些局面推理在这里搞乱了。

如果X的正文(或返回类型)中不需要Ytrouble,则可以使用存在类型来避免引用它们:

def trouble[Z <: R[_, _]](r: Z) {}

如果确实需要它们,可以使用视图绑定:

def trouble[X <: U[X, Y], Y <: E[X, Y], Z <% R[X, Y]](r: Z) {}

请参阅上面链接的答案,了解其工作原理。