I have a trait Book that looks like this
trait Book{
val sqlTableName;
def getAll: Seq[ Book ] = { magicSQLFn( $"SELECT * FROM $sqlTableName" ) }
}
I have two derived types:
class Fiction extends Book{ val sqlTableName = "fiction" }
class NonFiction extends Book{ val sqlTableName = "nonfiction" );
I need to get Seq[Fiction]
when I call getAll
on an instance of Fiction
, say fiction1
. I know one way to do would be to do a .map( _.asInstanceOf[ Fiction ] )
. However, is there a way to avoid this altogether?
Actually, I realize the less wrong way to do would be to be able to define a companion object for Fiction
that extends Book
so that I can call getAll
on that object (as opposed to doing it on an instance), however, in that case, I'm not sure how I'll convert the individual elements in the returned sequence to instances of Fiction
class because Fiction
class will no longer derive from Book
.
Should I have two differently named Book
traits? One that is the super of these objects, and the other that is the super class of these classes?
EDIT: @Travis Brown's reply solves my initial problem. If anyone have comments on how to handle this using companion objects and not class instances, that'd be great too!
This is more or less the classic use case for F-bounded polymorphism, which allows you to refer to the specific subtype in methods on the supertype:
trait Book[B <: Book[B]] {
val sqlTableName;
def getAll: Seq[B] = { magicSQLFn( $"SELECT * FROM $sqlTableName" ) }
}
class Fiction extends Book[Fiction] { val sqlTableName = "fiction" }
class NonFiction extends Book[NonFiction] { val sqlTableName = "nonfiction" )
(This assumes that your magicSQLFn
will return something with the appropriate static type, but it is magic, after all.)
F-bounded polymorphism has its detractors, and there are issues to watch out for, but it's a fairly widely used pattern in both Scala and Java.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With