Why can't I add a field to scala implicit class?
Why can't you add the field to the implicit class?
for example the below code does not store the originalName field
object Implicit1 {
implicit class AI1(c: Class[_]) {
private var originalName = ""
def setOriginalName(str: String) = originalName = str
def getOriginalName=originalName
}
}
source to share
An implicit class is a class that can be implicitly called as a wrapper for your class (in this case, an instance Class
). He absolutely can have var
; your code works great in that sense.
The problem is that you have no way to get the instance AI1
. So, create it, upload var
it and then throw it away. It is not part of the original c: Class[_]
; there are no monkey patches here. It just saves you typing (new AI1(c)).whicheverMethodYouPutOnAI1
.
And if you have a way to receive AI1
, you cannot receive c
again as you wrote it. AI1
is not a proxy for Class[_]
, it just has an instance. And right now your getter and setter don't add anything, just expose var. So you might have meant something like
implicit class AI1(val underlying: Class[_]) {
var originalName = ""
def hasName = this
}
Now you can do something like
val named = "fish".getClass.hasName
named.originalName = "salmon"
println(s"${named.originalName} ${named.underlying}")
source to share
As Rex Kerr pointed out, the implicit class will be instantiated whenever needed. Hence, every time you get a new instance of the implicit class, the value stored in the field is lost.
scala> val clz = "str".getClass
clz: Class[_ <: String] = class java.lang.String
scala> val impA: AT1 = clz
impA: Imp.AT1 = Imp$AT1@2d96543c
scala> val impB: AT1 = clz
impB: Imp.AT1 = Imp$AT1@7a560583
scala> impA == impB
res2: Boolean = false
Here's a workaround:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Imp {
object AT1 {
val clzBuffer = collection.mutable.Buffer[Class[_]]()
val strBuffer = collection.mutable.Buffer[String]()
def setName(clz: Class[_], name: String): Unit = {
val ind = clzBuffer indexWhere (_ eq clz)
if(ind == -1) {
clzBuffer += clz
strBuffer += name
}
else {
strBuffer(ind) = name
}
}
def getName(clz: Class[_]): String = {
val ind = clzBuffer indexWhere (_ eq clz)
if(ind == -1) "" else strBuffer(ind)
}
}
implicit class AT1(c: Class[_]) {
def originalName: String = AT1.getName(c)
def originalName_=(name: String) = AT1.setName(c, name)
}
}
// Exiting paste mode, now interpreting.
defined object Imp
scala> import Imp._
import Imp._
scala> val clz = "str".getClass
clz: Class[_ <: String] = class java.lang.String
scala> clz.originalName
res0: String = ""
scala> clz.originalName = "IamHere"
clz.originalName: String = IamHere
scala> clz.originalName
res1: String = IamHere
source to share
Summarizing and abstracting a bit, I use the following approach:
implicit class class_extension(val class_to_extend: class_to_extend) {
import class_extension._
private val fields = class_extension_fields_map.getOrElseUpdate(class_to_extend, class_extension_fields(default_value_1, default_value_2))
def a = fields.a
def a_=(value: Int) = fields.a = value
def b = fields.b
def b_=(value: Int) = fields.b = value
}
object class_extension {
private case class class_extension_fields(var a: Int, var b: Boolean)
private val class_extension_fields_map = new HashMap[class_to_extend, class_extension_fields]
}
This basically allows you to "extend" classes in a specific context, for example. adding fields to all nodes of the chart to apply a specific algorithm.
source to share