Implement addition in abstract Scala class


Implement addition in abstract Scala class



I have the below abstract class and its two subclasses:


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)



Can I somehow implement a "generic" addition in BoundedNumber even though it's abstract and cannot be instantiated?


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)
}
}



Or am I bound to (pun intended) implement addition in both subclasses thus duplicating code?





What is the main concern here? Are you trying to avoid adding code to the subclasses, or are you just trying to avoid having the add logic in two places? Is the solution allowed to add code to the subclasses?
– Tim
Jul 3 at 7:33


add





There are more than 2 subclasses and I would like to avoid adding duplicate-ish code anywhere.
– bugfoot
Jul 3 at 15:42




3 Answers
3



You cannot instantiate abstract class, However you can specify an abstract method in BoundedNumber that create new instance with required updated value.


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)
}





You can also set both copy and + to return/take instances of the subclass (and get rid of the require(this.getClass == that.getClass), which is... not a great way of enforcing type safety) by F-bounded polymorphism (twitter.github.io/scala_school/advanced-types.html#fbounded)
– Astrid
Jul 3 at 6:34


copy


+


require(this.getClass == that.getClass)





This answer coupled with @Astrid's comment to make the code more type safe I can solve my original program, but code "duplication" (of copy) remains, which I guess is unavoidable with abstract class methods that return concrete subclass instances.
– bugfoot
Jul 7 at 6:54


copy



If your goal is compile-time type safety in the sense that both operands of + must be of the same concrete type and that + also returns the same concrete type, you can declare an abstract type and constructor which will be implemented by every concrete subclass. + can then be defined in the abstract class:


+


+


+


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)
}



Now the compiler will correctly infer that the parameter for + on Percentage must be of type Percentage and that the result will be of type Percentage.


+


Percentage


Percentage


Percentage



Create a concrete class inside the abstract class and return that:


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)
}
}



This gives a generic implementation of + without adding any code to the subclasses.


+





Please give an explanation for the downvote, since I clearly answered the question correctly.
– Tim
Jul 3 at 10:10






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

JMeter fails on beanshell imports

Why in node-red my HTTP POST no receive payload from inject?

PHP contact form sending but not receiving emails