【问题标题】:How to avoid awful type casts working with path dependent types?如何避免糟糕的类型转换与路径相关类型一起使用?
【发布时间】:2012-10-30 21:55:43
【问题描述】:

我是 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
  }
} 

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

【问题讨论】:

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


    【解决方案1】:

    您可以在 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)
    

    【讨论】:

    • 非常感谢您回答 Travis,但您的解决方案仅在类范围内有效,在方法范围内(如我最初公开的示例)它不起作用。先前编译的行:val modBImpl = new ModBImpl(modAImpl);,现在抱怨:类型不匹配; found : ModAImpl required: M forSome { type M <: moda a="this.A}">b.set(a) 行现在抱怨: type mismatch;找到:modAImpl.A 需要:this.A
    • 在这种情况下,您可以显式提供类型参数 (val modBImpl = new ModBImpl[modAImpl.A](modAImpl))。这并不令人满意,但它有效,而且比强制转换更好。
    • 抱歉,Travis,但您刚刚编写的代码行无法编译。尽管如此,请假设 trait ModA 有很多成员,而不仅仅是 A。我需要 ModB 上可用的所有 ModA 成员。
    【解决方案2】:

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

    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-02-17
      • 2020-01-18
      • 1970-01-01
      • 2019-10-04
      • 2014-11-26
      • 1970-01-01
      • 2013-09-28
      • 2020-12-29
      • 1970-01-01
      相关资源
      最近更新 更多