【问题标题】:How to solve the inheritance of conflicting members in Scala如何解决Scala中冲突成员的继承
【发布时间】:2018-09-17 20:04:21
【问题描述】:

我是 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 类继承了冲突的成员: 字符串类型的特征 T2 中的值 f1 和 String 类型的特征 T3 中的变量 f1 (注意:这可以通过在 C2 类中声明覆盖来解决。) C2 类使用 T3 扩展 T2{} "

那么如果 C2 扩展具有相同字段名称的特征,应该如何更改? 感谢您的帮助。

【问题讨论】:

  • class C2 extends T2 with T3 {override val f1: String = ...}

标签: scala


【解决方案1】:

接受的答案是正确的,但请记住,建议的模式很奇怪,并且可能导致在非平凡情况下难以理解的错误。根据我的经验,覆盖非抽象的 vals 只会给你带来麻烦

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

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.f1 成为vals,你最好使用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 完全绕过了这个问题。

【讨论】:

  • 很好的建议。这些对以后的学习很有帮助。谢谢。
【解决方案2】:

您可以手动解决歧义:

  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

【讨论】:

    【解决方案3】:

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

    这里的问题是你在两个特征中都有相同的字段,编译器无法解析你要使用哪个特征的哪个属性。因此,您可以通过使其对编译器显式来解决问题。

    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}
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多