Реализовать дополнение в абстрактном классе Scala - PullRequest
0 голосов
/ 03 июля 2018

У меня есть следующий абстрактный класс и два его подкласса:

abstract class BoundedNumber(val lowerBound: Double,
                             val upperBound: Double,
                             val value: Double) {
  require(value >= lowerBound && value <= upperBound)
}

final case class Percentage(override val value: Double)
  extends BoundedNumber(0, 100, value)

final case class Probability(override val value: Double)
  extends BoundedNumber(0, 1, value)

Можно ли как-то реализовать "общее" дополнение в BoundedNumber, даже если оно абстрактное и не может быть создано?

abstract class BoundedNumber(val lowerBound: Double,
                             val upperBound: Double,
                             val value: Double) {
  def +(that: BoundedNumber): BoundedNumber = {
    require(this.getClass == that.getClass)
    // This of course won't compile:
    new BoundedNumber(lowerBound, upperBound, value + that.value)
  }
}

Или я обязан (каламбур) реализовать добавление в обоих подклассах, дублируя код?

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Создайте конкретный класс внутри абстрактного класса и верните это:

abstract class BoundedNumber(val lowerBound: Double,
                             val upperBound: Double,
                             val value: Double) {

  class ConcreteBoundedNumber(lowerBound: Double,
                              upperBound: Double,
                              value: Double) extends BoundedNumber(lowerBound, upperBound, value)

  def +(that: BoundedNumber): BoundedNumber = {
    require(this.getClass == that.getClass)

    new ConcreteBoundedNumber(lowerBound, upperBound, value + that.value)
  }
}

Это дает общую реализацию + без добавления кода в подклассы.

0 голосов
/ 03 июля 2018

Если ваша цель - безопасность типов во время компиляции в том смысле, что оба операнда + должны быть одного и того же конкретного типа и что + также возвращает один и тот же конкретный тип, вы можете объявить абстрактный тип и конструктор, который будет осуществляться каждым конкретным подклассом. + может быть определено в абстрактном классе:

abstract class BoundedNumber(val lowerBound: Double, val upperBound: Double, val value: Double) {
    require(value >= lowerBound && value <= upperBound)
    type Self <: BoundedNumber
    def make(value: Double): Self

    def +(that: Self): Self = make(value + that.value)
}

final case class Percentage(override val value: Double) extends BoundedNumber(0, 100, value) {
    type Self = Percentage
    def make(value: Double): Self = Percentage(value)
}

final case class Probability(override val value: Double) extends BoundedNumber(0, 1, value) {
    type Self = Probability
    def make(value: Double): Self = Probability(value)
}

Теперь компилятор правильно сделает вывод, что параметр для + в Percentage должен иметь тип Percentage, а результат будет иметь тип Percentage.

0 голосов
/ 03 июля 2018

Вы не можете создать экземпляр абстрактного класса, однако вы можете указать абстрактный метод в BoundedNumber, который создает новый экземпляр с требуемым обновленным значением.

  abstract class BoundedNumber(val lowerBound: Double,
                               val upperBound: Double,
                               val value: Double) {
    require(value >= lowerBound && value <= upperBound)

    def copy(value: Double): BoundedNumber

    def +(that: BoundedNumber): BoundedNumber = {
      require(this.getClass == that.getClass)
      that.copy(value + that.value)
    }
  }

  final case class Percentage(override val value: Double) extends BoundedNumber(0, 100, value) {
    override def copy(value: Double): BoundedNumber =  Percentage(value)
  }

  final case class Probability(override val value: Double) extends BoundedNumber(0, 1, value){
    override def copy(value: Double): BoundedNumber = Probability(value)
  }
...