0

When running the following as as scala script I get an InstantiationException. But when put it inside an class extending App and then compiling with scalac and running with scala it works. Any ideas?

class A
val foo = classOf[A]
foo.newInstance()
Alejandro Pedraza
  • 537
  • 1
  • 4
  • 13

1 Answers1

1

TL;DR Try foo.getConstructors()(0).newInstance(this), or use your own multiline shebang


The InstantiationException suggests that your call newInstance() is invoking a non-existent constructor.

Here is a similar script, printing your constructors:

#!/bin/sh
exec scala "$0" "$@"
!#
class A
val foo = classOf[A]
foo.getConstructors.foreach(c => println("Constructor: " + c))

Producing:

Constructor: public Main$$anon$1$A(Main$$anon$1)

Thus, an instance of an anonymous class must be passed to A's constructor.

It turns out, Scala scripts are run by a ScriptRunner, that wraps your script with an anonymous object (adapted from scala source, and Programming Scala):

object Main {
  def main(args: Array[String]): Unit = {
    new AnyRef {
      // Your script code is inserted here.
    }
  }
}

Thus passing the instance of your outer anonymous class, this, to A's constructor allows instantiation of your new instance of inner class A.

As far as I know, there's no way to create a static inner class within the scala language. Any constructors (created by the scala compiler) in a basic script will require the outer instance. The other options, that I'm aware of, include bytecode manipulation to dynamically create a new constructor for the class (way overkill, imho), or reimplementing the ScriptRunner.

During the single exec line in the shell script, Scala's implementation of ScriptRunner:

  • Strips the header of multiline shebang for you down to #!
  • Wraps your code in the anonymous outer class template listed above
  • Writes a temporary scala file
  • Compiles the scala file to a temporary directory
  • Executes the compiled code
  • Cleans up the temporary directory and script
  • Exits the program with an exit code

You could write your own external bit of code that also does the same, in any language you want, and then exec your external code on one line, also. Or you can embed the functionality inside the script itself using multiline shebang syntax, like this example adapted from the Go sample on this page:

Debug version, printing commands via sh -x, and not actually deleting anything:

#!/bin/sh -x
scriptfile=`basename $0`
classname="${scriptfile%.*}"
scalafile="${classname}.scala"
sed -e '1,12d' -e "s/%scala_class_name%/${classname}/" < "$0" > $scalafile
scalac $scalafile
echo todo: rm $scalafile
scala $classname "$@"
STATUS=$?
echo todo: rm "${classname}*.class"
exit $STATUS
######## Scala code starts on line 13
object %scala_class_name% {
    def main(args: Array[String]) {
        class A
        val foo = classOf[A]
        println(foo.newInstance())
    }
}

The reason I included a debug version is that you probably want to just use this as a starting point. For example, one might want to do more setup or cleanup depending on what else is in the script, including:

  • Edit the jvm args, including the classpath
  • Create a whole temporary directory if there are multiple classes expected in the scripts
  • More sed/awk of the embedded scala itself depending on passed in arguments
  • etc.

That said here's a "it should just work, but I make no guarantees" version of the above, that cleans up after itself:

#!/bin/sh
scriptfile=`basename $0`
classname="${scriptfile%.*}"
scalafile="${classname}.scala"
sed -e '1,12d' -e "s/%scala_class_name%/${classname}/" < "$0" > $scalafile
scalac $scalafile
rm $scalafile
scala $classname "$@"
STATUS=$?
rm "${classname}*.class"
exit $STATUS
######## Scala code starts on line 13
object %scala_class_name% {
    def main(args: Array[String]) {
        class A
        val foo = classOf[A]
        println(foo.newInstance())
    }
}

Citations:

Conflict of interest disclaimers for linking to commercial books:

Original answerer does not work for Oreilly, but is an avid consumer. ;)

Community
  • 1
  • 1
kshakir
  • 2,142
  • 1
  • 15
  • 9
  • Wonderful, thanks. Any idea on how to create such an inner class (A) that has a no-args constructor? Turns out I need to pass a Class reference to a library that itself calls newInstance(), so I can't modify that call... – Alejandro Pedraza Feb 19 '14 at 23:31
  • 1
    Updated with an example that allows foo.newInstance(), and used updated rep to finally fix links. Thanks! – kshakir Feb 20 '14 at 07:04