Reputation: 3032
Let's say I have a case class with a lazy member
case class Person(name: String, surname: String) {
lazy val initials: String = name(0) + "." + surname(0) + "."
}
And I have a universal function which converts it into Map
def getCCParams(cc: AnyRef) =
cc.getClass.getDeclaredFields.map { f =>
f.setAccessible(true)
f.getName -> f.get(cc)
}.toMap
now I create a person and get its values
val JohnSmith = Person("John", "Smith")
val res = getCCParams(JohnSmith)
println(res)
thus i get result
HashMap(initials -> null, name -> John, surname -> Smith)
initials equal null because it was not called.
Is there any way to activate lazy value inside getCCParams
function? The list of lazy members I can pass as a parameter
def getCCParams(cc: AnyRef, lazyMembers: List[String] = List("initials")) = ...
Thank you
Upvotes: 2
Views: 165
Reputation: 3032
Thanx a lot Krzysztof and Mario, finally i did like this
def getCCParams(cc: AnyRef, lazyFields: Set[String] = Set()) =
{
val cl = cc.getClass
val pairs = cl.getDeclaredFields flatMap { f: Field =>
f.getName match {
case name: String if name.startsWith("bitmap$") => None
case name: String =>
if (lazyFields.contains(name)) {
cl.getMethod(name).invoke(cc)
}
f.setAccessible(true)
Some(name -> f.get(cc))
}
}
pairs.toMap
}
val JohnSmith = Person("John", "Smith")
val res = getCCParams(JohnSmith, Set("initials"))
it allows to choose which members i want to activate, which i dont, and allows not include bitmap$ to result
Upvotes: 2
Reputation: 48430
Krzysztof's answer works because lazy val initials = "J.S."
expands to something like
lazy var initials: String = _;
var bitmap$0: Boolean = _;
private def initials$lzycompute(): String = {
if (!bitmap$0)
{
initials = ("J.S.": String);
bitmap$0 = true
};
initials
};
lazy def initials(): String =
if (!bitmap$0)
initials$lzycompute()
else
initials;
where we see corresponding public initials
as well as private initials$lzycompute
methods which actually sets var initials
.
Upvotes: 4
Reputation: 22635
Lazy vals are initialized when the accessor method is called the first time. So maybe you could just call these methods instead of trying to get directly to fields?
def getCCParams(cc: AnyRef) = {
val clazz = cc.getClass
clazz.getDeclaredFields.flatMap { f =>
Try(clazz.getMethod(f.getName)) //get the accessor method by name of field
.toOption
.map(m => f.getName -> m.invoke(cc))
}.toMap
}
Result:
Map(initials -> J.S., name -> John, surname -> Smith)
Upvotes: 4