【问题标题】:Scala method call with generic arguments appears not polymorphic - what is wrong带有泛型参数的 Scala 方法调用似乎不是多态的 - 出了什么问题
【发布时间】:2012-11-25 06:56:47
【问题描述】:

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

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 类型参数。我明白这一点 - 当我指定类型时,它可以,但它很麻烦。有没有办法以某种方式轻推/帮助编译器,以使这不再是一个问题?

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

提前致谢!

【问题讨论】:

    标签: scala scala-2.9


    【解决方案1】:

    您对 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) {}
    

    那我相信你会发现你的 main 方法编译没有改变。

    顺便说一句,代码中的每个分号都可以在不改变含义的情况下删除。

    【讨论】:

    • 是的,我知道分号。我只是太习惯了。问题是 R、U、E 和 R2、U2 和 E2 真的是在 Java 中(不能改变这个事实),其余的是在 Scala 中。我有一点机会对 Java 代码进行小的修改,但是如果我从 R 中删除类型参数,那么在那个 Java 世界中,所有的地狱都会崩溃。
    • 这种方法的问题是trouble 上的XY 类型参数没有意义(并且可能会误导)。它们与R 的类型成员没有任何关联,将被推断为Nothing
    【解决方案2】:

    请参阅 this answer(以及我在此处链接的答案),了解 Scala 类型推断的局限性,这些局限性在这里造成了混乱。

    如果您在trouble 的主体(或返回类型)中不需要XY,则可以使用存在类型来完全避免引用它们:

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

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

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

    请参阅上面链接的答案,了解为什么会这样。

    【讨论】:

    • 谢谢!这就是我需要的!
    • 很高兴它有帮助!请注意,您也可以通过编写def trouble[X &lt;: U[X,Y], Y &lt;: E[X,Y]](r: R[X, Y]) {} 之类的内容来简化一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    相关资源
    最近更新 更多