0

So I have defined a Scala object using the following code:

trait SomeTrait {
  def someMethod(): Unit
}

package somepackage1 {
  object Impl1 extends SomeTrait {
    def someMethod(): Unit = { }
  }
  object Impl2 extends SomeTrait {
    def someMethod(): Unit = { }
  }
}

I want to access the object given its fully qualified name i.e. somepackage1.Impl2. A method something like following:

package object somewhereElse {
  def getImpl(qualifiedName: String): SomeTrait = {
    ???
  }
}

What should be the code as part of getImpl method?

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
vatsal mevada
  • 5,148
  • 7
  • 39
  • 68
  • 2
    That would require reflection, but this rather seems an XY question – cchantep Aug 28 '18 at 10:18
  • Not an XY. That is precisely what the requirement is. Given the fully qualified name, I need to invoke a method of the fetched object. I know that with class I can have `Class.forName("SomeClass").newInstance()`. However, should the same code work for Scala Object also as a Scala object is a singleton? – vatsal mevada Aug 28 '18 at 10:26
  • 4
    Definitely looks like an XY problem. But here you go: `Class.forName(theName).getField("MODULE$").get()` – Dima Aug 28 '18 at 10:44
  • 3
    In your code, there are no objects of type `SomeObject`. There is only one singleton object of type `SomeObject.type`. But if you *already* know what this object is, and you have *already* encoded it in the return type of your `getObject` method, then I don't see what else you can possibly want. – Andrey Tyukin Aug 28 '18 at 11:15
  • @AndreyTyukin updated the code snippet to add more details. This might clarify the need. I feel Dima's code should serve my purpose. – vatsal mevada Aug 28 '18 at 12:24
  • 1) You cannot `extend` from singleton objects; 2) `someMethod` overrides nothing (2x); 3) You cannot have two `package somepackage1;` declarations ending with semicolon in same file, use `package name { ... }` instead; 4) `getImpl` was probably supposed to be a `def`; 5) Text did no longer match the updated code. Everything broken -> repaired. – Andrey Tyukin Aug 28 '18 at 12:59
  • @AndreyTyukin thank you for the repair. After the repair now does my requirement makes sense? Is there any way to handle this other than reflection? – vatsal mevada Aug 28 '18 at 13:11
  • 1
    Well, define "reflection". As soon as you are trying to translate a runtime `String` value into an instance of compile-time defined class, this whole task automatically becomes "reflection". You could of course simply collect all the available classes manually, and then hardcode their names and singleton objects in `getImpl`, but that would be just "poor man's reflection". – Andrey Tyukin Aug 28 '18 at 13:26

1 Answers1

3

Here is a solution that uses the Java Reflection API (you might want to use the newer Scala Reflection API instead):

trait SomeTrait {
  def someMethod(): Unit
}

package somepackage1 {
  object Impl1 extends SomeTrait {
    def someMethod(): Unit = { println("impl 1") }
  }
  object Impl2 extends SomeTrait {
    def someMethod(): Unit = { println("impl 2") }
  }
}

package object somewhereElse {
  def getImpl(qualifiedName: String): SomeTrait = {
    val clazz = Class.forName(qualifiedName + "$")
    clazz
      .getField("MODULE$")
      .get(clazz)
      .asInstanceOf[SomeTrait]
  }
}

object Demo {
  def main(args: Array[String]): Unit = {
    somewhereElse.getImpl("somepackage1.Impl2").someMethod()
  }
}

It's quite similar to Dima's comment above, with two minor differences: note the '$' appended to class name, and also the .get(clazz) instead of just .get() (without the clazz, it throws java.lang.NoSuchFieldException: MODULE$).

When saved to file f.scala, and compiled and invoked using

scalac f.scala && scala Demo

it prints:

impl 2
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93