首页 文章

如何解决Scala中冲突成员的继承问题

提问于
浏览
1

我是Scala的初学者 . 我被告知“特质中的一个字段可以是具体的或抽象的” .

trait T2 {
  val f1: String = "T2f1"
}

trait T3 {
  val f1: String = "T3f1"
}

class C2 extends T2 with T3{}

object Test2 extends App {
  val c2 = new C2
  println(c2.f1)
}

当我运行上面的代码时,编译器打印了一些错误消息:

“类C2继承冲突成员:String类型的特征T2中的值f1和String类型的特征T3中的变量f1(注意:这可以通过在类C2中声明覆盖来解决 . )类C2使用T3 {}扩展T2

那么如果C2扩展具有相同字段名称的特征,应该改变什么呢?谢谢您的帮助 .

3 回答

  • 1

    您可以手动解决歧义:

    trait T2 {
        val f1: String = "T2f1"
      }
    
      trait T3 {
        val f1: String = "T3f1"
      }
    
      class C2 extends T2 with T3 {
        override val f1: String = "T2f1"
      }
    

    要么

    trait T2 {
        val f1: String = "T2f1"
      }
    
      trait T3 {
        val f1: String = "T3f1"
      }
    
      class C2 extends T2 with T3 {
        override val f1: String = "T3f1"
      }
    

    如果是的话,编译器可以通过线性化自动完成

    trait T2 {
        val f1: String = "T2f1"
      }
    
      trait T3 extends T2 {
        override val f1: String = "T3f1"
      }
    
      class C2 extends T2 with T3
    

    Conflicting fields in Scala Traits

  • 1

    接受的答案是正确的,但请记住,建议的模式很奇怪,可能导致很难理解非平凡案例中的错误 . 根据我的经验,重写非抽象 vals 只会让你遇到麻烦 .

    问题是初始化代码是定义的类/特征的构造函数的一部分 . 这意味着在创建 C2 的实例时将执行初始化 T2.f1T3.f1 的代码:

    trait T2 {
        val f1: String = {
            println("T2")
            "T2f1"
        }
    }
    
    trait T3 {
        val f1: String = {
            println("T3")
            "T3f1"
        }
    }
    
    class C2 extends T2 with T3 {
        override val f1: String = {
            println("C2")
            "T3f1"
        }
    }
    
    new C2 // Will print "T2", then "T3", then "C2"
    

    如果初始化代码有任何重要的副作用,这可能会导致难以追踪的错误!它还有一个缺点,就是强迫你在 C2 中重复一些 T3 的代码 .

    如果您不是绝对需要 T2.f1T3.f1vals ,那么最好使用 defs 来避免抽象 vals 中的初始化代码:

    trait T2 {
        def f1: String = "T2f1"
    }
    
    trait T3 {
        def f1: String = "T3f1"
    }
    
    class C2 extends T2 with T3 {
        override val f1: String = "C2f1" // You can keep this a def if you like
    }
    

    如果你真的需要 f1 成为一个val,例如如果您需要一个稳定的值来在模式匹配语句中使用它,您可以使用以下内容:

    trait T2 {
        val f1: String
        protected def computeF1: String = {
            println("T2")
            "T2f1"
        }
    }
    
    trait T3 {
        val f1: String
        protected def computeF1: String = {
            println("T3")
            "T3f1"
        }
    }
    
    class C2 extends T2 with T3 {
        override val f1: String = computeF1 // You can keep this a def if you like
        override protected def computeF1: String = super[T3].computeF1
    }
    
    new C2 // Only prints "T3" once
    

    最后一个解决方案有点冗长,但它通过避免覆盖非抽象 val 来完全绕过问题 .

  • 0

    建议的例子是正确的,但尽量避免循环依赖,它也可能导致一些严重的问题 .

    看到这里的问题是你在两个特征中都有相同的字段,编译器无法解析你将使用哪个特性的属性 . 因此,您可以通过使其明确编译器来解决问题 .

    Trait A{
    val x:String=“ABC”}
    
    Trait B extends A{
    override val x: String=“DEF”}
    
    class AB extends B{
    override val f1: String = super[T3].x}
    

相关问题