I want to create a program that generates random things when requested, such as letters in the below example. I've defined an abstract class with a companion object to return one of the subclasses.
abstract class Letter
class LetterA extends Letter {
override def toString = "A"
}
class LetterB extends Letter {
override def toString = "B"
}
class LetterC extends Letter {
override def toString = "C"
}
object Letter {
def apply = RNG.d(3) match {
case 1 => new LetterA
case 2 => new LetterB
case 3 => new LetterC
}
override def toString = "Random Letter"
}
The dream is that I'll be able to use Letter
to get a random letter of type A, B or C. But with this code it just gives me the letter object. Letter.apply
gives me A, B or C but is ugly.
If I change the apply
function to be apply()
then things are a little better as I can then use Letter()
to get one of my random letters, but Letter
still gives me the object.
Is there anything I can do so that assigning something to Letter
actually resolves to one of LetterA
, LetterB
or LetterC
?
Answer to your question is no, you can't distinguish between Letter.type and Letter.apply, so you need a workaround.
I would suggest you to use Type-Class pattern for it, it's much more extenible than adding random-generating apply method to companion object
trait RandomGenerator[T] {
def gen: T
}
implicit object RandomLetter extends RandomGenerator[Letter] {
private val rnd = new Random()
def gen = (rnd.nextInt(3) + 1) match {
case 1 => new LetterA
case 2 => new LetterB
case 3 => new LetterC
}
}
def random[T: RandomGenerator]: T =
implicitly[RandomGenerator[T]].gen
println(random[Letter])
println(random[Letter])
println(random[Letter])
You could provide an implicit conversion from Letter.type
to Letter
in the companion object:
implicit def asLetter(companion: Letter.type): Letter = companion.apply
Then you can use Letter
to get an object that can be implicitly converted to the Letter
class. For example:
val x: Letter = Letter
val letters: Seq[Letter] = Seq.fill(10)(Letter)
But remember that this way you'll always have to ascribe the object to the class in use.
Note: I don't think this is a particularly good idea, but I just wanted to show that it is possible!
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