Generics
A generic class keeps its type parameters with their bounds, and an applied type keeps its arguments. Both are visible in Scala.js, where the JVM's generic signatures do not exist.
Parameters
Type variables with bounds
typeParameters holds a TypeVariableDescriptor per parameter; its bounds are the names of its upper bounds.
- E
- scala.Nothing, demo.Entity
- K
- scala.Nothing, scala.Any
scala
trait Entity { def id: String }
class Repository[E <: Entity, K](val name: String)
ReflectMacros.reflect[Repository[?, ?]].typeParameters.collect {
case variable: TypeVariableDescriptor => variable.name -> variable.bounds.toList
}
Arguments
Applied types
reflectType of an applied type returns a ParameterizedTypeDescriptor. Its rawType is the ClassDescriptor, its typeArguments the arguments in order.
- reflectType[Map[String, List[Int]]].typeName
- scala.collection.immutable.Map[java.lang.String, scala.collection.immutable.List[scala.Int]]
- typeArguments(1).typeName
- scala.collection.immutable.List[scala.Int]
scala
ReflectMacros.reflectType[Map[String, List[Int]]] match
case parameterized: ParameterizedTypeDescriptor =>
parameterized.typeArguments(1) // itself parameterized: List[Int]
case other => other
Properties
Generic property types
A property's propertyType is a TypeDescriptor too, so a List[String] field tells you its element type.
- reflect[Box[?]].getProperty("value").map(_.propertyType.typeName)
- Some(T)
Parameters stay parameters
The descriptor of a generic class describes its declaration: a property of type T says T. Reflect the applied type with reflectType when you need the argument.