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

如何解决Scala中冲突成员的继承问题,scala,Scala,我是Scala初学者。有人告诉我,“特质中的一个领域可以是具体的,也可以是抽象的” 当我运行上面的代码时,编译器输出了一些错误消息: " 类C2继承冲突成员: 字符串和类型的特征T2中的值f1 字符串类型的特征T3中的变量f1 (注意:这可以通过在C2类中声明重写来解决。) 类C2用T3{}扩展T2 " 那么,如果C2扩展了具有相同字段名的特征,那么应该改变什么呢? 谢谢您的帮助。您可以手动解决歧义: trait T2 { val f1: String = "T2f1" }

我是Scala初学者。有人告诉我,“特质中的一个领域可以是具体的,也可以是抽象的”

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

" 类C2继承冲突成员: 字符串和类型的特征T2中的值f1 字符串类型的特征T3中的变量f1 (注意:这可以通过在C2类中声明重写来解决。) 类C2用T3{}扩展T2 "

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

您可以手动解决歧义:

  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 extends T2 {
    override val f1: String = "T3f1"
  }

  class C2 extends T2 with T3

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

问题是初始化代码是已定义类/特征的构造函数的一部分。这意味着在创建
C2
的实例时,将执行初始化
T2.f1
T3.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.f1
T3.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
完全绕过了这个问题

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

这里的问题是,您在trait中有相同的字段,而编译器无法解析要使用的trait的哪个属性。因此,您可以通过将问题显式化给编译器来解决问题

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}

类C2使用T3扩展T2{override val f1:String=…}
您的代码将无法编译。我只是想解释一下这些建议。这些都有助于进一步的学习。非常感谢。
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
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}