Как разрешить наследование конфликтующих элементов в Scala

avatar
hawkcraft
8 апреля 2018 в 08:00
3486
3
2

Я новичок в 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 наследует конфликтующие члены: значение f1 в свойстве T2 типа String и переменная f1 в трейте T3 типа String (Примечание: это можно решить, объявив переопределение в классе C2.) класс C2 расширяет T2 с помощью T3{} "

Так что же следует изменить, если C2 расширяет свойства, имеющие одинаковое имя поля? Спасибо за помощь.

Источник
mfirry
8 апреля 2018 в 08:25
0

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

Ответы (3)

avatar
Dmytro Mitin
8 апреля 2018 в 08:30
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

Конфликтующие поля в Scala Traits

avatar
Raman Mishra
8 апреля 2018 в 12:27
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}
Dmytro Mitin
8 апреля 2018 в 13:22
0

Ваш код не будет компилироваться. coderhelper.com/questions/45935672/…

Raman Mishra
8 апреля 2018 в 13:27
0

Я просто пытался объяснить концепцию

avatar
francoisr
8 апреля 2018 в 10:59
2

Принятый ответ правильный, но имейте в виду, что предлагаемый шаблон странный и может привести к труднопонятным ошибкам в нетривиальных случаях. По моему опыту, переопределение неабстрактного vals приведет только к проблемам.

Проблема в том, что код инициализации является частью конструктора определенного класса/признака. Это означает, что код, инициализирующий как T2.f1, так и T3.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"

Если код инициализации имеет какой-либо важный побочный эффект, это может привести к трудно отслеживаемым ошибкам! Он также имеет тот недостаток, что вам приходится повторять часть кода T3 в C2.

.

Если вам не обязательно, чтобы T2.f1 и T3.f1 были vals, возможно, вам лучше использовать defs, чтобы избежать абстрактного кода инициализации vals:<2794693>179379

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.

hawkcraft
9 апреля 2018 в 01:00
0

Отличные предложения. Они полезны для дальнейшего обучения. Спасибо.