Skip to content
Anjunar/ DOCS

Generics

Plain reflection tells you a method returns T. scala-universe tells you what T is for the class you asked about.

Return types

Overridden with a concrete type

The subclass overrides a generic method. The resolved method is the subclass's, and its return type is concrete.

scala
abstract class Box[T] { def getValue: T } class StringBox extends Box[String] { override def getValue: String = "hello" } val method = TypeResolver.resolve(classOf[StringBox]).findMethod("getValue") println(method.returnType.raw) // class java.lang.String
Inherited members

Declared generic, resolved concrete

Members inherited without an override are resolved against the starting type as well: fields, return types and parameters.

scala
abstract class Repository[E] { var items: java.util.List[E] = new java.util.ArrayList[E]() def save(entity: E): E = entity } class UserRepository extends Repository[User] val repository = TypeResolver.resolve(classOf[UserRepository]) val save = repository.findMethod("save", classOf[Object]) println(save.parameters.head.parameterType.name) // User println(save.returnType.name) // User println(repository.findField("items").fieldType.typeArguments(0)) // ResolvedClass(User)
Look up by erased parameters

findMethod matches the JVM signature, so a parameter of type E is found as classOf[Object]. The resolved parameterType then says User.

Unresolved variables

Start from the concrete type

Resolving the generic class itself leaves its type variables unbound. In 1.0.4 the raw class of an unbound variable is the class that declares it, not its bound.

scala
val generic = TypeResolver.resolve(classOf[Repository[?]]) val save = generic.findMethod("save", classOf[Object]) println(save.returnType.underlying) // E println(save.returnType.raw) // class example.Repository
Resolve through the subclass

Code that reads raw or name of a member type should start from a concrete class such as UserRepository. Starting from Repository[?] gives a type variable, whose raw class says nothing about its values.

Type arguments

Collections know their elements

typeArguments of a parameterized type are ResolvedClasses themselves. That is what lets json-mapper create the right element type for each JSON array.

scala
def elementType(property: AbstractProperty): ResolvedClass = property.propertyType.typeArguments.headOption.getOrElse(TypeResolver.resolve(classOf[Object]))