简体   繁体   中英

Can I use Scala TypeTags and ClassTags to traverse an object model without instances?

My goal is to consume the structure of an object model by traversing its classes. This wouldn't be possible in Java because erasure prevents me from knowing the element type of a collection at runtime. I don't really understand ClassTags and TypeTags yet. Is there a way to use those to resurrect the element type of a collection even if I don't have access to instances?

UPDATE: An integral part of this problem for me is that I want to examine arbitrary JavaBeans and do not have a list of possible input classes upfront. Examples I've seen using TypeTags seem to always create a TypeTag for a class that is explicitly named at compile time. Can I use type tags to determine the element type of a collection without having an a priori list of possible element types?

There is several ways:

1) Runtime. JVM loses info about polymorphic types (generics) because of erasure, but if you need that kind of reflection for your objects, you can introduce you can use typeTag for your root object and go deeper then:

def getReflectedType[T: TypeTag](val object: T)= typeTag[T]
val m = runtimeMirror(getClass.getClassLoader)

case class Aaa[T](a: T)
case class Bbb[T](l: List[T])
val a = Bbb(List.empty[Aaa[Int]])

So here you can access that info:

scala> getReflectedType(a).tpe
res35: reflect.runtime.universe.Type = Bbb[Aaa[Int]]

scala> getReflectedType(a).tpe.foreach(println)
Bbb[Aaa[Int]]
$line58.$read.$iw.$iw.$iw.$iw.type
$line58.$read.$iw.$iw.$iw.type
$line58.$read.$iw.$iw.type
$line58.$read.$iw.type
$line58.$read.type
$line58.type
<root>
Aaa[Int]
$line59.$read.$iw.$iw.$iw.$iw.type
$line59.$read.$iw.$iw.$iw.type
$line59.$read.$iw.$iw.type
$line59.$read.$iw.type
$line59.$read.type
$line59.type
<root>
Int
scala.type

So you can basically access that info.

Some helpers might be useful:

scala> getReflectedType(a).tpe.typeSymbol.asClass
res55: reflect.runtime.universe.ClassSymbol = class Bbb

Provides you ClassSymbol, that has an API (note that they almost alwys split functionality to some separate API so if you see a reflection entity without any members - try to look for an EntityApi) :

http://www.scala-lang.org/api/2.11.0-M4/index.html#scala.reflect.api.Symbols $ClassSymbolApi

scala> getReflectedType(a).tpe.declarations.toList.map(_.typeSignature)
warning: there was one deprecation warning; re-run with -deprecation for details
res67: List[reflect.runtime.universe.Type] = List(=> scala.List[T], scala.List[T], (l: scala.List[T])Bbb[T], [T](l: scala.List[T])Bbb[T], [T]=> scala.List[T] @scala.annotation.unchecked.uncheckedVariance, => java.lang.String, => scala.Int, (x$1: scala.Int)scala.Any, => Iterator[scala.Any], (x$1: scala.Any)scala.Boolean, ()scala.Int, ()java.lang.String, (x$1: scala.Any)scala.Boolean)

scala> getReflectedType(a).tpe.declarations.toList.map(x => x.name -> x.typeSignature).mkString("\n")
warning: there was one deprecation warning; re-run with -deprecation for details
res69: String =
(l,=> scala.List[T])
(l ,scala.List[T])
(<init>,(l: scala.List[T])Bbb[T])
(copy,[T](l: scala.List[T])Bbb[T])
(copy$default$1,[T]=> scala.List[T] @scala.annotation.unchecked.uncheckedVariance)
(productPrefix,=> java.lang.String)
(productArity,=> scala.Int)
(productElement,(x$1: scala.Int)scala.Any)
(productIterator,=> Iterator[scala.Any])
(canEqual,(x$1: scala.Any)scala.Boolean)
(hashCode,()scala.Int)
(toString,()java.lang.String)
(equals,(x$1: scala.Any)scala.Boolean)

provides you type signatures of members, I couldn't find a convenient apy to match signature to type-parameter , so you'll have to do it manually ( asSeenFrom might help).

2) Compile-time. You might not know full type of structure when you get it inside your function. So you can use WeakTypeTag with macroses then. It will provide as much info as possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM