tags:

views:

197

answers:

2

Given an instance of a class, we can obviously return its name:

trait MixedInClassDiscovery {
  val className = this.getClass.getName
}

class AClass extends MixedInClassDiscovery {
  ...
  this.className // returns "AClass"
  ...
}

But this way uses reflection, once for every instance of AClass. Can the same be done once for every class, instead?

One solution which comes to mind is to mix it into companion objects instead of classes themselves.

+1  A: 

You can do it with the pimp my lib pattern. Create an implicit conversion from AnyRef to e.g. ClassNameAdder. But it is not recommended to do create such an implicit conversion at this level of the type hierarchy.

Anyway here comes the code:

scala> class ClassNameAdder(ref: AnyRef) { def className = ref.getClass.getName }
    defined class ClassNameAdder

scala> implicit def anyref2classnameadder(ref: AnyRef) = new ClassNameAdder(ref: AnyRef)
anyref2classnameadder: (ref: AnyRef)ClassNameAdder

scala> "foo".className
res6: java.lang.String = java.lang.String

scala> new Object().className
res7: java.lang.String = java.lang.Object

scala> List(1,2,3).className
res8: java.lang.String = scala.collection.immutable.$colon$colon

scala> class MyClass
defined class MyClass

scala> val myClass = new MyClass
myClass: MyClass = MyClass@1398044

scala> myClass.className
res9: java.lang.String = MyClass
michael.kebe
No, this code still does a reflection call for every instance. Actually, for every `className` call, though this can be trivially fixed.
Alexey Romanov
+1  A: 

I can't think of any way to do it with no extra overhead. You could do it with companion objects, however, and a couple of extra pieces of work:

object Example {
  trait Discovery {
    def companion: Discovered
    def className: String = companion.className
  }
  trait Discovered extends Discovery {
    override lazy val className = {
      println("Getting class name!")  // To see how many times we're called
      this.getClass.getSuperclass.getName
    }
  }
  class Test extends Discovery {
    def companion = Test
  }
  object Test extends Test with Discovered {}
}

And here we see that this works:

scala> val a = new Example.Test
a: Example.Test = Example$Test@17e4c97

scala> val b = a.className
Getting class name!
b: String = Example$Test

scala> val c = a.className
c: String = Example$Test

but it comes at rather a price: you need to not only decorate the class with Discovery but also implement the companion method and write the companion object (which need not have the same name, incidentally) for every class.

Rex Kerr