1

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?

4
  • 1
    Why do you need to do this? runtime reflection is discouraged for a number of reasons. Are you completely sure that your meta-problem can only be solved using reflection. Commented Sep 17, 2020 at 1:39
  • @LuisMiguelMejíaSuárez I would personally avoid using reflection as much as I can. but this approach is what the senior engineer in my team is pushing for right now Commented Sep 17, 2020 at 1:41
  • 1
    That is sad to hear, I am pretty sure someone will come and provide a good answer to this problem but still, I would encourage you to open another question describing the meta-problem, it may help you to convince that "senior" about a different approach, or at least it would help you in the future to be a better developer. Commented Sep 17, 2020 at 1:44
  • Thank you, I'll explore more and tell him what I think about this whole thing Commented Sep 17, 2020 at 1:51

1 Answer 1

1

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).

Sign up to request clarification or add additional context in comments.

5 Comments

Except I cannot do that. :( I simplified the code there, but the actual function that takes modifiedItem is of generic type T, and we need to find out the type at runtime using reflection e.g. def myFunc[T:ClassTag](modifiedItem: T)(implicit tag:ru.TypeTag[T]): T = { val theType = getTypeTag(modifiedItem).tpe // blah blah do the rest here }
@Hush See update with staticClass. If you still can't do that then you should edit your question providing a minimal code corresponding your actual use case.
That's what I was looking for. Thank you!
@Hush Actually, it's better to use toType instead of typeSignature. See update.
@Hush One more way is val instanceMirror = m.reflect(modifiedItem) val theType = instanceMirror.symbol.toType. It's better because doesn't use operations on strings (replace).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.