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
  }
}

      

+3


source to share


3 answers


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}")

      

+6


source


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

      

+1


source


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.

0


source







All Articles