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