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

问题描述 投票:0回答:3

在 Java 1.6.0_21 中,下面的第一个示例编译得很好,我认为这是因为参数类型界限是裸露的。 也就是说,在下面的“Z extends Zen”绑定中,Java允许 Zen 作为原始、非泛型类型的名称(相当于运行时“擦除”) 类型)。 这可能是错误和糟糕的,但它也可能有用,或者至少是古怪的 坐公交车回家的美好时光:

public class CorefTest {

    public static interface Tao<Z extends Zen> {
    }

    public static interface Zen<T extends Tao> {
    }
}

在 Scala 2.8.0.final 中,下面通过 CleanOceanWithFish 编译得很好,显示 一些基本类型参数连接。 但当我们认识到禅与道是相互依存的 泛型类型,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”和“[_]”的存在类型对我没有帮助)。 我认为获得禅和道 在 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”和“_

<:" in Scala.

由于禅和道都不是协变的,所以禅和道之间存在可替代性问题。这对我来说在 2.8.1 下编译得很好:
generics scala scala-2.8 scala-java-interop
3个回答
3
投票
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]]

我想,这就是你所需要的:

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

此代码片段由 Scala 2.8.1 成功编译

更新

不幸的是,在 scala 中扩展 CorefTest.Zen

CorefTest.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以进行详细讨论

克里斯的回答并不能完全满足我的需求。我希望内部具有可变数据,而这对于协变类型是不可能的。

0
投票
如果指定“self”类型,则可以绕过协方差的需要

trait Tao[Z <: Zen[Z,T], T <: Tao[Z,T]] {self: T => var t: Z = _ def link(t: Z) = { this.t = t t.t = this } } trait Zen[Z <: Zen[Z,T], T <: Tao[Z,T]] {self: Z => var t: T = _ } class Test1 extends Tao[Test2, Test1] { def test=t // Test2 } class Test2 extends Zen[Test2, Test1] { def test=t // Test1 } //Illegal inheritance, self-type Test3 does not conform to T //This can only succeed if Test3 also extends Test1 class Test3 extends Tao[Test2, Test1] { def test=t }

它有点冗长,需要您传入扩展特征的类作为类型参数,但目前无法自动显示该类型(在该位置使用
this.type
是不可能的)

我认为,除了冗长之外,该解决方案还提供了最佳的可用性和类型安全性。

© www.soinside.com 2019 - 2024. All rights reserved.