How can we extract the name/type of “P” preferrably in compile-time?
Given the following traits:
trait Plugin
trait NetworkPlugin extends Plugin
trait StoragePlugin extends Plugin
and the event class:
case class Event[P <: Plugin](
// How do we know the name give the plugin type (P)?
val eventType: Class[P] = ???
val eventName: String = ???
)
It sounds like you’re skirting dangerously close to “you can’t do that, because of erasure” (which is a big and important topic when you’re trying to do things like this to generics). Suffice it to say, there are a lot of things you can’t just casually do with type parameters.
That said, you can get at least some information about P by using the ClassTag type class, which creates a synthetic implicit parameter with information about the specified type. It’s imprecise (which can matter when trying to do more-sophisticated things), but if all you need is the Class object and name of an ordinary trait, it will suffice. I use it moderately often to do things like this.
import scala.reflect.ClassTag
trait Plugin
trait NetworkPlugin extends Plugin
trait StoragePlugin extends Plugin
case class Event[P <: Plugin : ClassTag]() {
// How do we know the name give the plugin type (P)?
val eventType: Class[?] = summon[ClassTag[P]].runtimeClass
val eventName: String = eventType.getSimpleName()
}
val networkEvent = Event[NetworkPlugin]()
val storageEvent = Event[StoragePlugin]()
println(networkEvent.eventName)
println(storageEvent.eventName)
You can create an array whose element types are designated by the type variable:
import scala.reflect.ClassTag
object TypeErasureTag:
// No ClassTag available for T
// def createArray[T](length: Int, element: T) = new Array[T](length)
def createArray[T: ClassTag](length: Int, element: T): Array[T] =
val arr = new Array[T](length)
if length > 0 then arr(0) = element
arr
@main def runTypeErasureTag():Unit =
println(createArray(1, "howdy").toList.toString)
I am posting another solution that seems to be more simple and more reliable than the previous solutions:
trait Plugin
case class PluginEvent[T <: Plugin](clazz: Class[T])
trait NetworkPlugin extends Plugin
trait FirewallPlugin extends Plugin
class Listener {
def isInterested(message: Any): Boolean = message match {
case PluginEvent(clazz) => {
if (clazz == classOf[NetworkPlugin]) {
println("Found NetworkPlugin clazz:" + clazz)
true
} else if (clazz == classOf[FirewallPlugin]) {
println("Found FirewallPlugin clazz:" + clazz)
true
} else {
println("Only partial match")
false
}
}
case _ => {
println("No match at all")
false
}
}
}
@main
def main(): Unit = {
val listener = new Listener()
val networkPluginEvent = PluginEvent(classOf[NetworkPlugin])
listener.isInterested(networkPluginEvent)
println("-" * 80)
val firewallPluginEvent = PluginEvent(classOf[FirewallPlugin])
listener.isInterested(firewallPluginEvent)
}
results:
Found NetworkPlugin clazz:interface NetworkPlugin
--------------------------------------------------------------------------------
Found FirewallPlugin clazz:interface FirewallPlugin