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?
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.
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
addlogic in two places? Is the solution allowed to add code to the subclasses?– Tim
Jul 3 at 7:33