How to get the type of a type parameter?

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 = ???
)

val networkEvent = EventNetworkPlugin
val storageEvent = EventStoragePlugin

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.

Here’s a fully-worked version of what I think you’re trying to do here:

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)
3 Likes

Thank you very much for quick and good response. Indeed, it is exactly what I needed and works perfectly in my case.

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)