如何避免使用路径依赖类型的可怕类型转换?

时间:2012-11-11 00:15:14

标签: scala type-mismatch path-dependent-type

我是Scala的新手,不知道为什么我必须在下面的代码中对路径依赖类型做一个(不直观的)类型转换。 (我不喜欢getter,setter或nulls,他们在这里分开操作并消除错误的来源)

// Module A public API
class ModA {
  trait A
}

// Module B public API that depends on types defined in Module A
class ModB(val modA: ModA) {
  trait B {
    def getA: modA.A;
    def setA(anA: modA.A);
  }
}

// One implementation of Module A
class ModAImpl extends ModA {
  class AImpl extends A
}

// One implementation of Module B
class ModBImpl(mod: ModA) extends ModB(mod) {
  class BImpl extends B {
    private[this] var privA: modA.A = _;
    override def getA = privA;
    override def setA(anA: modA.A) = privA = anA;
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    // wiring the modules
    val modAImpl = new ModAImpl;
    val modBImpl = new ModBImpl(modAImpl);

    // wiring objects
    val a = new modAImpl.AImpl;
    val b = new modBImpl.BImpl;
    b.setA(a); //don't compile and complain: type mismatch;  found: modAImpl.A  required: modBImpl.modA.A

    //i have to do this horrible and coutnerintuitive cast to workaround it
    b.setA(a.asInstanceOf[modBImpl.modA.A]);

    var someA: modAImpl.A = null;
    someA = b.getA; // don't compile with same reason
    someA = b.getA.asInstanceOf[modAImpl.A]; // horrible cast to workaround

    println(a == b.getA); // however this prints true
    println(a eq b.getA); // this prints true too
  }
} 

我已经阅读过有关单例类型的信息,以便在两种类型相同时通知编译器,但我不知道如何在此处应用它。 提前谢谢。

2 个答案:

答案 0 :(得分:5)

您可以在ModB类型上添加类型参数:

class ModA { trait A }

class ModB[AA](val modA: ModA { type A = AA }) {
  trait B {
    def getA: AA
    def setA(anA: AA)
  }
}

class ModAImpl extends ModA { class AImpl extends A }

class ModBImpl[AA](
  mod: ModA { type A = AA }) extends ModB(mod) {
  class BImpl extends B {
    private[this] var privA: AA = _
    override def getA = privA
    override def setA(anA: AA) = privA = anA
  }
}

类型推断全部按照需要进行:

scala> val modAImpl = new ModAImpl
modAImpl: ModAImpl = ModAImpl@7139edf6

scala> val modBImpl = new ModBImpl(modAImpl)
modBImpl: ModBImpl[modAImpl.A] = ModBImpl@1dd7b098

scala> val a = new modAImpl.AImpl
a: modAImpl.AImpl = ModAImpl$AImpl@4cbde97a

scala> val b = new modBImpl.BImpl
b: modBImpl.BImpl = ModBImpl$BImpl@63dfafd6

scala> b.setA(a)

答案 1 :(得分:0)

让我们从简化代码开始,消除不必要的复杂性。

class Aout {
    class Ain
}

class Bout(val link: Aout)  {
    class Bin(val field: link.Ain)
}

object Main {
    def main(args: Array[String]): Unit = {
        // wiring outer object
        val aout: Aout = new Aout;
        val bout: Bout = new Bout(aout);

        // wiring inner object
        val ain: aout.Ain = new aout.Ain;
        val bin: bout.Bin = new bout.Bin(ain); //don't compile and complain: type mismatch;  found: aout.Ain  required: bout.link.Ain
    }
}

答案:

编译器抱怨类型不匹配错误,因为他比较了赋值中涉及的两个声明类型的路径,并且它们是不同的。 编译器不够智能,无法注意到此时aout eq bout.link。你必须告诉他。 因此,替换行

val ain: aout.Ain = new aout.Ain;

使用

val ain: bout.link.Ain = new bout.link.Ain;

解决与路径有关的类型不匹配。

无论如何,仅在原始代码中更正类型的路径是不够的,因为还存在继承问题。 一种解决方法是像这样使类ModBImpl知道ModAImpl类:

class ModA {
    trait A
}

class ModB[M <: ModA](val modA: M) { // type parameter added
    trait B {
        def getA: modA.A;
        def setA(anA: modA.A);
    }
}

class ModAImpl extends ModA {
    class AImpl extends A
}

class ModBImpl(mod: ModAImpl) extends ModB(mod) { // changed type of `mod` parameter from `ModA` to `ModAImpl`

    class BImpl extends B {
        private[this] var privA: modA.A = _;
        override def getA: modA.A = privA;
        override def setA(anA: modA.A): Unit = privA = anA;
    }
}

object Main {
    def main(args: Array[String]): Unit = {
        val modAImpl = new ModAImpl;
        val modBImpl = new ModBImpl(modAImpl);

        val a: modBImpl.modA.AImpl = new modBImpl.modA.AImpl; // changed the path of the type
        val b: modBImpl.BImpl = new modBImpl.BImpl;
        b.setA(a); // here IntellijIde complains with a type mismatch error, but the ScalaIDE (eclipse) and the scala compiler work fine.
    }
}

如果您的业务规则不允许ModBImpl类拥有ModAImpl类的知识,请告诉我,以便我们找到另一种解决方案。