mccallum
mccallum

Reputation: 175

scala: How to get the class in its own constructor

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.)

Upvotes: 4

Views: 1247

Answers (6)

Randall Schulz
Randall Schulz

Reputation: 26486

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

Upvotes: 2

Silvio Bierman
Silvio Bierman

Reputation: 713

How about

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

Upvotes: 1

Geoff Reedy
Geoff Reedy

Reputation: 36011

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.

Upvotes: 1

Oleg Galako
Oleg Galako

Reputation: 1035

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)

Upvotes: 0

Randall Schulz
Randall Schulz

Reputation: 26486

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

Upvotes: 8

michid
michid

Reputation: 10814

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!

Upvotes: 2

Related Questions