I've used the accepted answer of this question to build little helper class to construct case classes from arrays of values:
construct case class from collection of parameters
As mentioned in the answer, inner case classes don't work. You get a
ScalaReflectionException: class X is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
I'm having trouble figuring out how to use an InstanceMirror as the exception explains. Here's my current REPL-able code (slightly more complex than needed because of the cache).
import scala.reflect.runtime.universe._
object ReflectUtil {
private val constructorCache = collection.mutable.HashMap[String, MethodMirror]()
def constructFromSeq[T: TypeTag](args: Seq[Any]): T = {
val tag = typeTag[T]
val strTag = tag.toString
var constructorMirror = constructorCache.getOrElseUpdate( strTag, {
val rm = runtimeMirror(getClass.getClassLoader)
val classSymbol = tag.tpe.typeSymbol.asClass
val classMirror = rm.reflectClass(classSymbol) // wrap with try/catch?
val constructorSymbol = tag.tpe.declaration(nme.CONSTRUCTOR).asMethod
classMirror.reflectConstructor(constructorSymbol)
})
constructorMirror(args: _*).asInstanceOf[T]
}
}
case class X(a:String, b:Int)
class Out {
case class Y(a:String, b:Int)
def buildY(arr:Array[Any]) = {
ReflectUtil.constructFromSeq[Y](arr)
}
}
val arr = Array[Any]("asdf", 1234)
ReflectUtil.constructFromSeq[X](arr) // this works
val inst = new Out
inst.buildY(arr) // this doesn't work
What exception implies is that you need an outer reference to construct an instance of an inner class (e.g. to construct Y you need an instance of Out). Then you could do rm.reflect(instanceOfOut).reflectClass....
Related
With scala 2.12.10
Suppose I want to implicitly convert at runtime a case class, in this case Special to a case class SpecialString. The implicit conversion is provided by a trait External. The name for SpecialString should be the declaration name of the class Special.
import scala.reflect.runtime.universe.{runtimeMirror, typeOf}
import scala.reflect.runtime.universe
case class Special(i: Int)
case class SpecialString(s: String)
trait External {
val rm = runtimeMirror(getClass.getClassLoader)
val im = rm.reflect(this)
val members = im.symbol.typeSignature.members
def specials: Iterable[universe.Symbol] = members.filter(_.typeSignature <:< typeOf[Special] )
implicit def SpecialIntToString(s: Special): SpecialString = {
val name = im.reflectField(specials.filter(x => im.reflectField(x.asTerm).get.asInstanceOf[Special] == s).head.asTerm).symbol.toString.replace("value ", "")
SpecialString(s"name = $name")
}
}
Currently I'm able to implicitly convert convert members declared inside a class extending the External trait.
class MyClass extends External {
val firstSpecial = Special(1)
val two = 2
val specialS: SpecialString = firstSpecial
}
class MySecondClass extends MyClass {
val specialS2: SpecialString = firstSpecial
}
val myClass = new MyClass
print(myClass.specialS) // SpecialString(name = firstSpecial)
But I'm not able to convert members that are declared in a super class
class MyClass {
val firstSpecial = Special(1)
val two = 2
val specialS: SpecialString = firstSpecial
}
class MySecondClass extends MyClass with External {
val specialS2: SpecialString = firstSpecial
}
val myClass = new MyClass
print(myClass.specialS)
val mySecondClass = new MySecondClass
print(mySecondClass.specialS2) // java.util.NoSuchElementException: next on empty iterator
Any help?
If you find necessary member by name rather than typeSignature (and it's actually found then) and print specials.head.typeSignature and typeOf[Special] you'll see why one is not a subtype of another
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.name == universe.TermName("firstSpecial") )
//members.filter(_.typeSignature.resultType <:< typeOf[Special] )
println(s"specials.head.typeSignature=${specials.head.typeSignature}=${universe.showRaw(specials.head.typeSignature)}")
println(s"typeOf[Special]=${typeOf[Special]}=${universe.showRaw(typeOf[Special])}")
println(s"specials.head.typeSignature <:< typeOf[Special]=${specials.head.typeSignature <:< typeOf[Special]}")
...
}
//specials.head.typeSignature=pckg.App.Special=NullaryMethodType(TypeRef(ThisType(pckg.App), pckg.App.Special, List()))
//typeOf[Special] =pckg.App.Special=TypeRef(ThisType(pckg.App), pckg.App.Special, List())
//specials.head.typeSignature <:< typeOf[Special]=false
Type of a nullary method returning Special is not a subtype of Special.
You should add resultType. Replace
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature <:< typeOf[Special] )
with
trait External {
...
def specials: Iterable[universe.Symbol] =
members.filter(_.typeSignature.resultType <:< typeOf[Special])
How to find class parameter datatype at runtime in scala
Suppose I have a MyItem trait and its companion object has an apply() function that creates a class instance called SubItem that extends from MyItem:
import scala.reflect.runtime.{universe => ru}
trait MyItem {
import MyItem._
def num: Option[Int]
}
object MyItem {
class SubItem(val num: Option[Int]) extends MyItem
def apply(num: Option[Int]): MyItem = new SubItem(num) // creates SubItem
}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val modifiedItem = MyItem(Some(11))
val theType = getTypeTag(modifiedItem).tpe
If you print out theType above, it will be MyItem.
At this point if you try to use reflection to modify the field num, it's not going to work because MyItem has num as a method, not a field (as in MyItem.SubItem):
val m = ru.runtimeMirror(modifiedItem.getClass.getClassLoader)
val numTermSymb = theType.decl(ru.TermName("num")).asTerm
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectField(numTermSymb) // not going to work
numFieldMirror.get
numFieldMirror.set(Some(999)) // my goal, if possible
Unfortunately, above will throw out scala.ScalaReflectionException: expected a field or an accessor method symbol, you provided method num.
Instead you should do the following:
val numTermSymb = theType.decl(ru.TermName("num")).asMethod
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectMethod(numTermSymb)
numFieldMirror() // returns `Some(11)`
But my goal is to access the SubItem class that extends MyItem and modify its field. How can I get an instance of the type MyItem and modify the field in MyItem.SubItem that MyItem's method num is accessing?
Replace
val theType = getTypeTag(modifiedItem).tpe
with
val theType = ru.typeOf[MyItem.SubItem]
if you know the name of class statically or with
val theType = m.staticClass("pckg.MyItem.SubItem").typeSignature
if you know the name of class dynamically.
Try
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).typeSignature
Actually, m.staticClass(className).typeSignature is AnyRef with pckg.MyItem {...} i.e. parents/decls of SubItem
theType =:= ru.typeOf[MyItem.SubItem] // false
so, although numFieldMirror.get/set work, it's better to use toType instead of typeSignature
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).toType
theType =:= ru.typeOf[MyItem.SubItem] // true
One more way is purely Scala-like
val instanceMirror = m.reflect(modifiedItem)
val theType = instanceMirror.symbol.toType
theType =:= ru.typeOf[MyItem.SubItem] // true
It's even better because doesn't use error-prone and implementation-dependent operations on strings (replace).
I have a program that can yield an abstract TypeTag when executed:
class TypeResolving extends FunSpec {
import org.apache.spark.sql.catalyst.ScalaReflection.universe._
val example = new Example
it("can convert") {
val t1 = implicitly[TypeTag[example.T]]
println(t1)
}
}
object TypeResolving {
class Example {
type T = Map[String, Int]
}
val example = new Example
}
The execution results in:
TypeTag[TypeResolving.this.example.T]
Since in this case example.T is already defined, I would also like to get the actual TypeTag:
TypeTag[Map[String,Int]]
How do I get there?
Try dealias
def dealias[T, T1](typeTag: TypeTag[T]): TypeTag[T1] = backward(typeTag.tpe.dealias)
val typeTag = implicitly[TypeTag[TypeResolving.example.T]] //TypeTag[TypeResolving.example.T]
val typeTag1 = dealias(typeTag) //TypeTag[scala.collection.immutable.Map[String,Int]]
val typeTag2 = implicitly[TypeTag[Map[String, Int]]] //TypeTag[Map[String,Int]]
val typeTag3 = dealias(typeTag2) //TypeTag[scala.collection.immutable.Map[String,Int]]
typeTag1 == typeTag3 //true
How to get the aliased type of a type alias in scala runtime?
Get a TypeTag from a Type? (backward is from here)
Why am I able to serialize this:
// Serialize: OK
case class ClassWithType2[T:TypeTag](x:T) {
val tpe:java.lang.reflect.Type = Util.toJavaClass[T]
}
... but not this
class TypeAware[T:TypeTag]() {
val tpe:java.lang.reflect.Type = Util.toJavaClass[T]
}
// Serialize: FAIL.
// No valid constructor for ClassWithType1
// in: java.io.ObjectStreamClass.checkDeserialize
case class ClassWithType1[T:TypeTag](x:T) extends TypeAware[T]
Both seem have the same constructor type prototype:
[T:TypeTag](x:T)
and both extend scala.Serializable and java.io.Serializable
val s1:Serializable = ClassWithType1(x=123)
val s2:Serializable = ClassWithType2(x=123)
val s3:java.io.Serializable = ClassWithType1(x=123)
val s4:java.io.Serializable = ClassWithType2(x=123)
Its there a way to implement TypeAware subclasses that:
avoid having to declare tpe in every subclass (as ClassWithType2 does)?
allows the object to be serialized
Here's the test harness
class TypesTest {
#Test
def serializeTypeTest(): Unit = {
val obj2:Object = ClassWithType2(x=123)
Util.copyBySerialization(obj2) // Success!
val obj1:Object = ClassWithType1(x=123)
Util.copyBySerialization(obj1) // Fail
}
}
object Util {
def toJavaClass[T:TypeTag]: Class[_] = {
val tpe = typeOf[T]
runtimeMirror(tpe.getClass.getClassLoader).runtimeClass(tpe.typeSymbol.asClass)
}
def copyBySerialization[T](obj: T): T = deserialize(serialize(obj))
def serialize[T](obj: T): Array[Byte] = {
val byteOut = new ByteArrayOutputStream()
val objOut = new ObjectOutputStream(byteOut)
objOut.writeObject(obj)
objOut.close()
byteOut.close()
byteOut.toByteArray
}
def deserialize[T](bytes: Array[Byte]): T = {
val byteIn = new ByteArrayInputStream(bytes)
val objIn = new ObjectInputStream(byteIn)
val obj = objIn.readObject().asInstanceOf[T]
byteIn.close()
objIn.close()
obj
}
}
Just quoting the Javadoc:
To allow subtypes of non-serializable classes to be serialized, the
subtype may assume responsibility for saving and restoring the state
of the supertype's public, protected, and (if accessible) package
fields. The subtype may assume this responsibility only if the class
it extends has an accessible no-arg constructor to initialize the
class's state. It is an error to declare a class Serializable if this
is not the case. The error will be detected at runtime.
The ctor for TypeAware includes the implicit parameter.
Edit: one idea is to make the type tag a member. Or similar. It doesn't save as much syntax.
abstract class TypeAware {
protected def tt: TypeTag[_]
def tpe:java.lang.reflect.Type = Util.toJavaClass(tt)
}
case class ClassWithType1[T](x:T)(implicit val tt: TypeTag[T]) extends TypeAware
Edit, more linx:
tech page
faq
your question
I implemented the code mentioned in Get companion object instance with new Scala reflection API (code from here https://gist.github.com/xeno-by/4985929).
object Reflection {
def getCompanionObject(caseclassinstance:Product):Any = {
import scala.reflect.runtime.{currentMirror => cm}
val classSymbol = cm.classSymbol(caseclassinstance.getClass)
val moduleSymbol = classSymbol.companionSymbol.asModule
val moduleMirror = cm.reflectModule(moduleSymbol)
moduleMirror.instance
}
}
This works fine for any standard class of case classes. Unfortunately in some classes of the project I get an Exception: scala.ScalaReflectionException: object Tensor is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror The exception is pretty clear, so I tried to change my code as follows:
object Reflection {
def getCompanionObject(caseclassinstance:Product):Any = {
import scala.reflect.runtime.{currentMirror => cm}
val classSymbol = cm.classSymbol(caseclassinstance.getClass)
val moduleSymbol = classSymbol.companionSymbol.asModule
val instanceMirror = cm.reflect(caseclassinstance)
val moduleMirror = instanceMirror.reflectModule(moduleSymbol)
moduleMirror.instance
}
}
But now I get a scala.ScalaReflectionException: expected a member of class Tensor, you provided object Prototype2.SPL.SPL_Exp.Tensor and I did not find out how to change the code to fix this. Any help is greatly appreciated!
Update: I provide some code for better reproducibility:
scala> trait SPL {
| case class Tensor()
| }
defined trait SPL
scala> val s = new SPL {}
s: SPL = $anon$1#165f5a4
scala> val t = s.Tensor()
t: s.Tensor = Tensor()
scala> object Reflection { /* as in the first code snippet*/}
defined module Reflection
scala> Reflection.getCompanionObject(t)
scala.ScalaReflectionException: object Tensor is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror
...
scala> object Reflection { /* as in the second code snippet*/}
defined module Reflection
scala> Reflection.getCompanionObject(t)
scala.ScalaReflectionException: expected a member of class Tensor, you provided object SPL.Tensor
...
You should have an instance of module. You can get mirror for Tensor symbol only from mirror of Spl.
trait Spl {
case class Tensor(s: String)
}
val spl = new Spl {}
val t = spl.Tensor("T")
// mirror for module spl
val moduleMirror = cm.reflect(spl)
// class symbol for Tensor
val instanceSymbol = cm.classSymbol(t.getClass)
// symbol for companion object
val companionSymbol = instanceSymbol.companionSymbol.asModule
// mirror for companion object symbol in spl module mirror
val companionMirror = moduleMirror.reflectModule(companionSymbol)
scala> companionMirror.instance
res0: Any = Tensor
You could get an instance of Spl using a bit of ugly magic:
val outer =
t.
getClass.
getFields.
find(_.getName == """$outer""").
get.
get(t)
You should not do so until you can.
def getCompanionObject(t: Product):Any = {
import scala.reflect.runtime.{currentMirror => cm}
val outerField = t.getClass.getFields.find(_.getName == """$outer""")
val moduleMirror = outerField.map{ _.get(t) }.map{ cm.reflect(_) }
val instanceSymbol = cm.classSymbol(t.getClass)
val companionSymbol = instanceSymbol.companionSymbol.asModule
val companionMirror =
moduleMirror.
map{ _.reflectModule(companionSymbol) }.
getOrElse{ cm.reflectModule(companionSymbol) }
companionMirror.instance
}