Please consider this code:
object ResponseType extends Enumeration {
val Listing, Album = Value
}
I can get the Symbol
referring to this object like so:
import reflect.runtime.universe._
val s = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol
Now, having this symbol, how can I get the actual ResponseType
object?
Types obtained through reflection can be instantiated by invoking their constructor using an appropriate “invoker” mirror (mirrors are expanded upon below). Let's walk through an example using the REPL: scala> case class Person(name: String) defined class Person scala> val m = ru. runtimeMirror(getClass.
A Manifest[T] is an opaque descriptor for type T. Its supported use is to give access to the erasure of the type as a Class instance, as is necessary for the creation of native Arrays if the class is not known at compile time.
Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method. Anything you define and can give a name to in Scala has an associated symbol.
scala> val moduleClass = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol
moduleClass: reflect.runtime.universe.Symbol = object ResponseType
scala> val module = moduleClass.owner.typeSignature.member(moduleClass.name.toTermName)
module: reflect.runtime.universe.Symbol = object ResponseType
scala> reflect.runtime.currentMirror.reflectModule(module.asModule).instance
res9: Any = ResponseType
Now, some explanation is in order, since this is quite an obscure implementation detail we've (yet!) been unable to abstract over in the public API.
For every object
Scala creates an underlying class that represents its signature, internally called module class. For example, if you compile object C
the compiler will generate C$.class
. That's exactly the module class.
Note that module classes are different from companion classes. Say, for case class C
, the compiler will generate three symbols: type C
, term C
and (another one) type C
, where the first type C
represents the class C (which contains auto-generated copy, productPrefix, productArity etc) and the second type C
represents a signature of object C (which contains auto-generated factory, extractor etc). There won't be any name clashes, because the module class isn't added to the symbol table directly and is only available through <module>.moduleClass
.
So what you actually get from your typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol
incantation is a symbol that stands for a module class. There's no function in the API that would get you to a module symbol from a module class. Internally in the compiler there surely is one, but we decided not to expose this implementation detail, because it might very well change soon.
To get to a source module you need to go to owner
, peek into the list of its members and look up an object there having the same name as the module class has. That's exactly what moduleClass.owner.typeSignature.member(moduleClass.name.toTermName)
does. One minor caveat is that if in the same scope you have a method with the same name, then member
will return an overloaded symbol, and you'll need to do something like .member(...).suchThat(_.isModule)
.
After that it's a piece of cake.
Edit. Actually we're thinking of introducing ClassSymbol.module
that would return source module symbol for a module class and NoSymbol otherwise. Quite probably this will end up in RC1. Follow the release notes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With