0
class Person(name: String, age: Int, numThings: Option[Int] = Some(15))

I can use Scala reflection to obtain defaults on a case class like this:

   val companionType: Type = classSymbol.companion.typeSignature
   val companionObject = currentMirror.reflectModule(classSymbol.companion.asModule).instance
   val companionMirror = currentMirror.reflect(companionObject)
   val defaultValueAccessorMirror =
   if (member.typeSignature.typeSymbol.isClass) {
     val defaultValueAccessor = companionType.member(TermName("apply$default$" + (index + 1)))
     if (defaultValueAccessor.isMethod) {
       Some(companionMirror.reflectMethod(defaultValueAccessor.asMethod))
     } else {
       None
     }
   } else {
     None
   }

This obtains the method in the generated companion object that, when called, coughs up the default value. Sadly, a non-case class doesn't appear to have this facility.

How can I obtain the default value for Person.numThings in the example above using either Scala or Java reflection?

Greg
  • 10,696
  • 22
  • 68
  • 98

1 Answers1

1

I think that it should be much easier to retrieve these default values through Java reflection, instead of this over-complicated Scala's reflect...

When compiled into a .class file, default parameter values are translated into static methods with specific suffixes in names. These values can be retrieved by invoking the respective method on the class reference.

So, for example, we have both a case and a non-case classes:

class Person(name: String, age: Int, numThings: Option[Int] = Some(15))

case class Item(id: Long, other: String = "unknown")

First we need to determine the ordinal indices of the params to retrieve defaults for. I do not know your use case, so let's suppose you know or calculated them. They will be 3 for Person and 2 for Item. Yes, they are not 0-based.

And this very short method retrieves the values:

private def extractDefaultConstructorParamValue(clazz: Class[_],
                                                iParam: Int): Any = {
  val methodName = "$lessinit$greater$default$" + iParam
  clazz.getMethod(methodName).invoke(clazz)
}

Calling them with

val defParamNonCase = extractDefaultConstructorParamValue(classOf[Person], 3)
val defParamCase = extractDefaultConstructorParamValue(classOf[Item], 2)

println(defParamNonCase)
println(defParamCase)

outputs

Some(15)
unknown
Antot
  • 3,904
  • 1
  • 21
  • 27