两个耦合的Scala泛型构造函数如何作为类型参数相互引用



在Java 1.6.0_21中,下面的第一个示例编译得很好,我认为这是因为参数类型界限是裸露的。也就是说,在下面的"Z扩展Zen"范围内,Java允许Zen作为原始的非泛型类型的名称滑过(相当于运行时"擦除")。类型)。这可能是错误的和不好的,但它也可能是有用的,或者至少是古怪的回家路上的美好时光:

public class CorefTest {
    public static interface Tao<Z extends Zen> {
    }
    public static interface Zen<T extends Tao> {
    }
}

在Scala 2.8.0中。最后,下面通过"清洁海洋与鱼"进行编译,显示一些基本的类型参数连接。但是当我们谈到禅和道是相互依存的泛型类型,Scala编译器拒绝我的编织构造。参见注释中的编译器错误。

package heaven.piece
class Lucky {
    trait Water {}
    trait CleanWater extends Water {}
    trait Sea [W <: Water] {}
    trait Fish[S <: Sea[CleanWater]] {}
    trait CleanOceanWithFish[F <: Fish[CleanOceanWithFish[F]]] 
                   extends Sea[CleanWater]{}
// Above code compiles fine, but the type constructor pair below doesn't compile 
    trait Tao[Z <: Zen[Tao[Z]]]{};
    trait Zen[T <: Tao[Zen[T]]]{};
}
// error: type arguments [Lucky.this.Tao[Z]] do not conform to trait Zen's 
//     type parameter bounds [T <: Lucky.this.Tao[Lucky.this.Zen[T]]]
// error: type arguments [Lucky.this.Zen[T]] do not conform to trait Tao's 
//     type parameter bounds [Z <: Lucky.this.Zen[Lucky.this.Tao[Z]]]

那么,我如何才能正确地将Scala(2.8.0)结在道与禅之间呢?

这当然是一个人为的例子,但我真正想要的是使用Scala来扩展我以上述形式工作的一些真实的Java类型(到目前为止,通过"forSome"one_answers"[_]"存在的类型对我没有帮助)。我认为获得禅和道在Scala中编译可能会为Java扩展指明方向。如果您能在回答中考虑Java扩展问题,那就更好了。谢谢你的帮助!

更新在Nikita S.和Kris N.非常有帮助的前两个答案之后发布

我从经验中学到了更多关于各种Java+Scala共同引用场景的知识。结果是,当我们希望 Java和Scala中的可互操作的协指类型时,那么这个Java构造:

public static interface JavaFunTao<JFZ extends JavaFunZen<? extends JavaFunTao<JFZ>>> {
    public JFZ consider(JFZ someZen, JavaFunTao<JFZ> otherTao);
}
public static interface JavaFunZen<JFT extends JavaFunTao<? extends JavaFunZen<JFT>>> { 
    public JFT meditate(JFT someTao, JavaFunZen<JFT> otherZen);
}

提供了比上面的第一个Java示例更具体的类型(避免了原始类型),然后在Scala中适当地扩展如下:

class HiFunTao[HFZ <: HiFunZen[  _ <: HiFunTao [HFZ]]] extends JavaFunTao[ HFZ] {
    override def consider(someZen: HFZ, otherTao: JavaFunTao[HFZ]) : HFZ = {
        println (this.toString() + " is considering " + someZen + " and " + otherTao);
        someZen
    }
}
class HiFunZen[HFT <: HiFunTao[ _ <:  HiFunZen [HFT]]] extends JavaFunZen[ HFT] {
    override def meditate(someTao: HFT, otherZen: JavaFunZen[HFT]) : HFT = {
        println (this.toString() + " is meditating on " + someTao + " and " + otherZen);
        someTao
    }
}

我验证了我们可以基于这些创建简单的具体类型,实例化它们,并调用它们的方法。Java和Scala的关键步骤是将有界通配符放置在类型参数树循环回当前声明类型的位置,即"?"java中的"extends"和Scala中的"_ <:"。

既然禅和道都不是协变的,那么你的禅和道之间就存在可替换性问题。

trait Tao[+Z <: Zen[Tao[Z]]]
trait Zen[+T <: Tao[Zen[T]]]

当然,如果你想考虑Z或T,那么这也不适合你,至少不完全像上面说的那样,因为协变类型参数在逆变位置的问题。你可以这样解决这个问题:

trait Tao[+Z <: Zen[Tao[Z]]] {
  def meditate[M >: Z](m: M) = ()
}
trait Zen[+T <: Tao[Zen[T]]] {
  def meditate[M >: T](m: M) = ()
}

,但这可能仍然会对您的实现产生您可能不想要的约束。至少,这是令人困惑的。:)

更新:

顺便说一下,这也避免了NSkvortsov的答案更新中提到的问题。这个编译得很好:
class Zazen extends Zen[Tao[Zazen]]

我想,这就是你需要的:

class MyClass {
  trait Tao[Z <: Zen[_]]{};
  trait Zen[T <: Tao[_]]{};
}

Scala 2.8.1编译成功的代码段

不幸的是,在scala中扩展CorefTest.ZenCorefTest.Tao是不可能的。以下是原因。

java中实现接口的唯一方法是使用原始类型:
public class TaoImpl<Z extends CorefTest.Zen> implements CorefTest.Tao<Z> { }
public class ZenImpl<T extends CorefTest.Tao> implements CorefTest.Zen<T> { }

可以像这样实例化类:

TaoImpl<ZenImpl> tao = new TaoImpl<ZenImpl>();
ZenImpl<TaoImpl> zen = new ZenImpl<TaoImpl>();

但是scala不支持原始类型。所以TaoImplZenImpl不能定义。请参阅此电子邮件主题和问题#2091和#1737进行详细讨论

最新更新