2

I have a number of objects (not classes) that manipulate databases, and I want to make a smaller helper class so I can do something like java my.helper.class my.database.class and execute the the run method.

For example, this compiles

trait A extends Runnable
class B extends A { def run() = println("run") }
object Test extends App {
  Class.forName(args(0)).newInstance().asInstanceOf[A].run()
}

And then does what I expect.

$scala Test B
run

This also compiles

trait A extends Runnable
object B extends A { def run() = println("run") }
object Test extends App {
  Class.forName(args(0)).newInstance().asInstanceOf[A].run()
}

But this happens:

$scala Test B
java.lang.InstantiationException: B
    at java.lang.Class.newInstance(Class.java:418)
    at Test$.delayedEndpoint$Test$1(Test.scala:9)
    at Test$delayedInit$body.apply(Test.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)
    at Test$.main(Test.scala:8)
    at Test.main(Test.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:68)
    at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:99)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:68)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:99)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:72)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Caused by: java.lang.NoSuchMethodException: B.<init>()
    at java.lang.Class.getConstructor0(Class.java:2971)
    at java.lang.Class.newInstance(Class.java:403)
    ... 28 more

Which makes sense, and I figured this would work:

$scala Test B$
java.lang.IllegalAccessException: Class Test$ can not access a member of class B$ with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:101)
    at java.lang.Class.newInstance(Class.java:427)
    at Test$.delayedEndpoint$Test$1(Test.scala:9)
    at Test$delayedInit$body.apply(Test.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:383)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)
    at Test$.main(Test.scala:8)
    at Test.main(Test.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:68)
    at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:99)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:68)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:99)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:72)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

But it also fails. I know I could just make all these static objects into classes, but that doesn't makes sense in this application, so I'm specifically looking for the elegant way to do this.

  • 3
    `B$`'s constructor is private, that is why your code failed. It is made private so that only one instance will be created. This one instance can be accessed through the static `MODULE$`. – wingedsubmariner May 28 '14 at 17:54
  • @wingedsubmariner - given `object Foo {}`, how can I use `MODULE$` to go from a String `"com.Foo$"` to getting the actual singleton instance? – Kevin Meredith Mar 01 '15 at 02:33
  • 1
    @KevinMeredith From a String? That would require reflection. I'd open up a new question for that. – wingedsubmariner Mar 02 '15 at 01:06

1 Answers1

2

I personally think the most elegant way is to not dynamically load things like this. Is it really that difficult to specify the valid input? This allows much greater flexibility with respect to where your instances of A come from.

object Test extends App {
  args(0) match {
    case "B" => B
    case "C" => 
      val someOtherConfig = args(1)
      new C(someOtherParam)
    case other => throw new Exception("invalid input")
  } run
}

I would use Scopt to parse parameters

drstevens
  • 2,903
  • 1
  • 21
  • 30
  • Yeah, I know that would work, but I was hoping there would be a better way to do this than a long, possibly non-exhaustive pattern match. –  May 29 '14 at 08:09