Inheritance
Every descriptor lists its base types by name. Registered descriptors answer subtype questions by name, without runtime classes, which is what plugin systems and polymorphic mapping need.
Base types
From the compiler
baseTypes is the linearization the compiler computed: superclasses and traits, up to Any.
- reflect[Dog].baseTypes
- demo.Animal, java.lang.Object, scala.Matchable, scala.Any
- reflect[Dog].isSubTypeOf("demo.Animal")
- true
- reflect[Dog].isSubTypeOf("demo.Entity")
- false
Lookups
ClassDescriptor as a directory
The ClassDescriptor object reads the global registry: find a descriptor by name, list the subtypes of a type, or check assignability between two names.
- ClassDescriptor.maybeForName("demo.Cat").map(_.simpleName)
- Some(Cat)
- subTypesOf("demo.Animal") without Animal itself
- List(Cat, Dog)
- ClassDescriptor.isAssignableFrom("demo.Cat", "demo.Animal")
- true
scala
ReflectRegistry.register(() => Dog("Rex"))
ReflectRegistry.register(() => Cat("Tom"))
ClassDescriptor.subTypesOf("demo.Animal")
ClassDescriptor.isAssignableFrom("demo.Cat", "demo.Animal")
Polymorphism
Create by a name from the outside
A type name in JSON, a URL or a configuration file becomes an instance, and the check that it is an Animal happens before anything is created.
- create("demo.Cat").map(_.name)
- Some(Tom)
- create("demo.Query")
- None
scala
def create(typeName: String): Option[Animal] =
Option.when(ClassDescriptor.isAssignableFrom(typeName, "demo.Animal"))(typeName)
.flatMap(ReflectRegistry.createInstance)
.collect { case animal: Animal => animal }