views:

216

answers:

6

I need access to the Class of the object being constructed in its own constructor (for various detailed reasons I don't think are relevant to my question).

I want something like this

class Foo(val i:Int)
class Bar extends Foo(this.getClass.getName.length)
val b = new Bar
println(b.i)

to print 3 ("Bar".length). But it doesn't. If the code above is inside some other object, "this" refers to that object. If the code above is not inside some other object (just in some package), the compiler complains with

error: this can be used only in a class, object, or template
class Bar extends Foo(this.getClass.getName)
                      ^

CLARIFICATION: I can't change Foo to use a val in its body instead of its constructor because Foo's API already exists and is fixed (so, yes, i must be a constructor parameter). It needs an integer argument at constructor time, but that integer can only be calculated with access to the Class.

(I know the example above is still silly and degenerate. If people care, I can explain in detail why I need the class in my real project, http://code.google.com/p/factorie)

Surely, the class of the object being constructed is known to the compiler and runtime during construction. With what syntax can I get it? (Is there no such syntax? If not, I wonder why. I'm surprised it there doesn't seem to be a simple, standard way to get this.)

+2  A: 

Not sure whether this is possible in a clean way. If you like hacks you could do

class Bar extends Foo((new Exception).getStackTrace.apply(0).getClassName)

However I strongly advertise against it!

michid
+7  A: 

A lazy val solves this problem:

object Early
{
    abstract class Foo { val name: String }

    class Bar extends Foo { lazy val name = getClass.getName }

    def
    main(args: Array[String]): Unit = {
        val b = new Bar
        println(b.name)
    }
}

Yields:

% scala Early
Early$Bar
Randall Schulz
By the way, the name `Early` is a leftover from my first attempt to solve this problem by using the "early definition" construct.
Randall Schulz
Randal, Thank you for your answer. Unfortunately it doesn't do the trick for me, as now better described in my edited question.
mccallum
A: 

What about this:

  class Foo(otherName: Option[String] = None) {
    val name = otherName.getOrElse(this.getClass.getName)
  }
  class Bar extends Foo()
  val b = new Bar
  println(b.name)
Oleg Galako
+1  A: 

You are going to have to explain your motivation for wanting to do this. Does name have to be a constructor parameter of Foo or can it be an abstract member? Does it have to be a val or will a def do.

You can do this

class Foo(val name: String)
class Bar extends Foo("") {
  override val name = getClass.getName
}

and new Bar().name will give Bar

But I suspect that if your true motivation is known then there is a better way to do what you really want.

Geoff Reedy
Geoff, Thank you. This does the trick. It preserves the original API of Foo, while allowing initialization of the val using this.getClass. I imagine that Bar takes the same amount of memory as Foo, but I'm not sure, and I don't know how to verify this.
mccallum
@mccallum you can use `javap -p classname` to show the fields that are in the classes. In this case, there will be a field for the value in both Foo and Bar, so there is extra storage. If the constructor parameter for Foo is an object, it'd be best to use null as the argument to minimize the storage requirements.
Geoff Reedy
+1  A: 

How about

class Bar extends Foo(classOf[Bar].getName.length)

Silvio Bierman
+2  A: 

This appears to satisfy your requirement without using a lazy val and without altering the base class:

scala> class Base(val name: String)
defined class Base

scala> class Derived extends Base(classOf[Derived].getName)
defined class Derived

scala> new Derived name
res0: String = Derived
Randall Schulz