I received a Scala jar and need to write a java program to use a method. The imported Scala method definitely works because it has been in production for years.
here is the program:
import com.company.VerifyApp // The imported Scala jar public class Validation { public static void main(String[] args) { VerifyApp app = new VerifyApp(); app.main(args); } }
Decompiled Scala code:
package com.company; public final class VerifyApp { public static void main(String[] var0) { VerifyApp$.MODULE$.main(var0); } }
Here are observations:
- Error message: cannot find symbol constructor VerifyApp()
- location: class com.company.VerifyApp
- Just a note that this location is correct
- VerifyApp, from the decompiled code, is a final class with no constructor (thus should have a default 0-arg constructor)
- I’m using IntelliJ and activated the “Include dependencies with “Provided” scope” already
- This is not a maven project, I just load the jar as external library
- When editing, IntelliJ shows no error or warning
- I checked tons of Stackoverflow posts with the same topic but couldn’t find anything useful
I’m wondering what could be the issue?
Edit:
IntelliJ warned me I’m instantiating a utility class and this mostly likely is an error. Since I’m a Java newbie, I Googled a bit and found out that maybe I could directly call VerifyApp.main(args)
without instantiating the object. This DOES pass the build, but I got a runtime error:
NoClassDefFoundError, caused by ClassNotFoundException
Now I’m out of trick.
Advertisement
Answer
Answering the original question, the reason for the error “cannot find symbol constructor VerifyApp()” has to do with a difference between Java and Scala.
It is true that the decompiled Java code, if re-compiled, would have a default, public, zero-argument constructor. This is a feature of the Java compiler: it inserts this default constructor into the compiled bytecode if no constructor is declared. Note that the bytecode interpreter itself does not insert a default constructor at runtime if it isn’t present in the compiled bytecode.
In the original Scala source code, this was defined as a singleton object. It would have looked something like this:
object VerifyApp { def main(args: Array[String]): Unit = ??? }
I don’t know what the actual method body looks like, because it would have been compiled into a class called VerifyApp$
, which you can see referenced in the decompiled source code in the question.
In general, the Scala compiler compiles object
definitions by:
- Creating a class with the name of the object followed by
$
, containing instance methods for methods defined on the object, a private constructor, and a static, finalMODULE$
field holding an instance of the class. - Creating a class with the plain name of the object containing static forwarder methods that invoke the matching method on that
MODULE$
instance, and no constructor.
You can see this by using the javap
program, for example:
javap -p -c com/company/VerifyApp.class com/company/VerifyApp$.class
You’ll get output similar to this:
Compiled from "VerifyApp.scala" public final class com.company.VerifyApp { public static void main(java.lang.String[]); Code: 0: getstatic #17 // Field com/company/VerifyApp$.MODULE$:Lcom/company/VerifyApp$; 3: aload_0 4: invokevirtual #19 // Method com/company/VerifyApp$.main:([Ljava/lang/String;)V 7: return } Compiled from "VerifyApp.scala" public final class com.company.VerifyApp$ { public static final com.company.VerifyApp$ MODULE$; public static {}; Code: 0: new #2 // class com/company/VerifyApp$ 3: dup 4: invokespecial #12 // Method "<init>":()V 7: putstatic #14 // Field MODULE$:Lcom/company/VerifyApp$; 10: return public void main(java.lang.String[]); Code: 0: getstatic #22 // Field scala/Predef$.MODULE$:Lscala/Predef$; 3: invokevirtual #26 // Method scala/Predef$.$qmark$qmark$qmark:()Lscala/runtime/Nothing$; 6: athrow private com.company.VerifyApp$(); Code: 0: aload_0 1: invokespecial #29 // Method java/lang/Object."<init>":()V 4: return }
The usual way to access these objects from Java is using the static forwarder methods without trying to instantiate the class. In this case, VerifyApp.main(args)
(as you discovered).
In the rare case that you do need to access the instance directly (for example, when it implements an interface that you need an instance of), you need to use the unsightly VerifyApp$.MODULE$
reference. It can also be assigned to another static final
field of a Java class or interface to provide a more readable name.