Reputation: 704
I need to get a Numeric[T]
if available (so I guess an Option[Numeric[T]]
) for the following situation:
class Foo[T]() {
def t:T
def doSomeProcessing():Unit = ... // This is where I need an Option[Numeric[T]]
}
class Bar {
def foos: Seq[Foo[_]]
def doSomethingWithFoos():Unit = {
for (foo <- foos) {
foo.doSomeProcessing()
}
}
}
I can add ClassTag
or TypeTag
as necessary / helpful, but I'd like to avoid an explicit listing of each and every possible Numeric, which is what I have right now:
val numOpt: Option[Numeric[T]] = classTag[T] match {
case longTag if longTag == classTag[Long] => Some(Numeric.LongIsIntegral.asInstanceOf[Numeric[T]])
case intTag if intTag == classTag[Int] => Some(Numeric.IntIsIntegral.asInstanceOf[Numeric[T]])
...
case _ => None
}
Is there a more elegant / comprehensive solution? Thanks!
Edit: I tried doing:
class Foo[T](implicit num: Numeric[T] = null) {
def t:T
val numOpt:Option[Numeric[T]] = Option(num)
}
But that didn't work (numOpt = None for T = Long), probably because Foo is actually part of a long class hierarchy with multiple this / super calls...
Update: the issue is that Foo actually has four constructors, and all need to take an (implicit num: Numeric[T] = null), but only one of them is allowed to have a default parameter. I guess I would have to find a way to get rid of three of them, or manually feed in null
as needed.
Upvotes: 0
Views: 78
Reputation: 7604
Try an implicit Numeric
parameter with a default value:
class Foo[T] private(val t: T)(val numeric: Option[Numeric[T]])
object Foo {
def apply[T](t: T)(implicit numeric: Numeric[T] = null) = new Foo(t)(Option(numeric))
}
You can use it as such
class Bar {
//Note that I've explicitly put in Foo[Int] here because it doesn't understand it otherwise (not sure why).
def foos: Seq[Foo[_]] = Seq(Foo("blah"), Foo[Int](39))
def doSomethingWithFoos():Unit = {
for (foo <- foos) {
println(foo.t)
foo.numeric match {
case Some(num: Numeric[foo.t.type]) => println(num.plus(foo.t, foo.t))
case None => println("Not numeric")
}
}
}
}
Output:
blah
Not numeric
39
78
<script src="https://scastie.scala-lang.org/CDSbri6WQwSYtIoR4Swl8A.js"></script>
If you don't want the boilerplate of an apply method, you can also define your own method to supply Option
instances and directly use an implicit in your constructor.
class Foo[T](val t: T)(implicit val numeric: Option[Numeric[T]])
implicit def implicitOption[T](implicit t: T = null): Option[T] = Option(t)
Link to Scastie: https://scastie.scala-lang.org/pUOw5tFNRtGZVbHNHx7QYw
Otherwise, you can use a nullable type instead of an Option
.
class Foo[T](val t: T)(implicit val numeric: Numeric[T] = null)
Link to Scastie: https://scastie.scala-lang.org/AOGNZSirT0C7Cy0Po6vZXw
Upvotes: 2