5

我是 Scala 新手,不知道为什么我必须在以下代码中执行与路径相关类型相关的(对我来说不直观)类型转换。(我不喜欢 getter、setter 或 null,它们是用来分离操作并消除错误来源的)

// 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
  }
} 

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

4

2 回答 2

6

您可以在类型上粘贴类型参数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)
于 2012-11-11T03:47:53.897 回答
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,请告诉我,以便我们找到另一个解决方案。

于 2019-09-22T02:18:46.960 回答