Java 找不到导入的 Scala 的符号 class
Java cannot find symbol for imported scala class
我收到了一个Scala jar,需要写一个java程序来使用一个方法。
导入的 Scala 方法肯定有效,因为它已经投入生产多年。
程序如下:
import com.company.VerifyApp // The imported Scala jar
public class Validation {
public static void main(String[] args) {
VerifyApp app = new VerifyApp();
app.main(args);
}
}
反编译的 Scala 代码:
package com.company;
public final class VerifyApp {
public static void main(String[] var0) {
VerifyApp$.MODULE$.main(var0);
}
}
以下是观察结果:
- 错误消息:找不到符号构造函数 VerifyApp()
- 位置:classcom.company.VerifyApp
- 请注意这个位置是正确的
- 来自反编译代码的 VerifyApp 是一个没有构造函数的最终 class(因此应该有一个默认的 0-arg 构造函数)
- 我正在使用 IntelliJ 并激活了“包含依赖于‘提供的’范围”
- 这不是一个 maven 项目,我只是将 jar 作为外部库加载
- 编辑时,IntelliJ 没有显示错误或警告
- 我检查了大量具有相同主题的 Whosebug 帖子,但找不到任何有用的东西
我想知道可能是什么问题?
编辑:
IntelliJ 警告我我正在实例化一个实用程序 class,这很可能是一个错误。由于我是 Java 新手,我用 Google 搜索了一下,发现也许我可以直接调用 VerifyApp.main(args)
而无需实例化对象。这确实通过了构建,但我遇到了运行时错误:
NoClassDefFoundError, caused by ClassNotFoundException
现在我没戏了
回答原问题,错误“找不到符号构造函数 VerifyApp()”的原因与 Java 和 Scala 之间的差异有关。
确实,反编译的 Java 代码如果重新编译,将有一个默认的 public 零参数构造函数。这是 Java 编译器的一个特性:如果没有声明构造函数,它将把这个默认构造函数插入到编译的字节码中。请注意,如果编译的字节码中不存在默认构造函数,则字节码解释器本身不会在运行时插入默认构造函数。
在最初的 Scala 源代码中,它被定义为 singleton object。它看起来像这样:
object VerifyApp {
def main(args: Array[String]): Unit =
???
}
我不知道实际的方法体是什么样的,因为它会被编译成一个名为 VerifyApp$
的 class,你可以在反编译的源代码中看到引用问题。
通常,Scala 编译器通过以下方式编译 object
定义:
- 创建一个 class,对象名称后跟
$
,包含对象上定义的方法的实例方法、私有构造函数和静态最终 MODULE$
包含 class. 实例的字段
- 使用对象的普通名称创建一个 class,该对象包含调用该
MODULE$
实例上的匹配方法的静态转发器方法,以及 no 构造函数.
你可以使用javap
程序看到这个,例如:
javap -p -c com/company/VerifyApp.class com/company/VerifyApp$.class
你会得到类似这样的输出:
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
}
从 Java 访问这些对象的常用方法是使用静态转发器方法而不尝试实例化 class。在这种情况下,VerifyApp.main(args)
(如您所见)。
在极少数情况下,您确实需要直接访问实例(例如,当它实现了您需要其实例的接口时),您需要使用难看的 VerifyApp$.MODULE$
引用。它也可以分配给 Java class 或接口的另一个 static final
字段以提供更具可读性的名称。
我收到了一个Scala jar,需要写一个java程序来使用一个方法。 导入的 Scala 方法肯定有效,因为它已经投入生产多年。
程序如下:
import com.company.VerifyApp // The imported Scala jar
public class Validation {
public static void main(String[] args) {
VerifyApp app = new VerifyApp();
app.main(args);
}
}
反编译的 Scala 代码:
package com.company;
public final class VerifyApp {
public static void main(String[] var0) {
VerifyApp$.MODULE$.main(var0);
}
}
以下是观察结果:
- 错误消息:找不到符号构造函数 VerifyApp()
- 位置:classcom.company.VerifyApp
- 请注意这个位置是正确的
- 来自反编译代码的 VerifyApp 是一个没有构造函数的最终 class(因此应该有一个默认的 0-arg 构造函数)
- 我正在使用 IntelliJ 并激活了“包含依赖于‘提供的’范围”
- 这不是一个 maven 项目,我只是将 jar 作为外部库加载
- 编辑时,IntelliJ 没有显示错误或警告
- 我检查了大量具有相同主题的 Whosebug 帖子,但找不到任何有用的东西
我想知道可能是什么问题?
编辑:
IntelliJ 警告我我正在实例化一个实用程序 class,这很可能是一个错误。由于我是 Java 新手,我用 Google 搜索了一下,发现也许我可以直接调用 VerifyApp.main(args)
而无需实例化对象。这确实通过了构建,但我遇到了运行时错误:
NoClassDefFoundError, caused by ClassNotFoundException
现在我没戏了
回答原问题,错误“找不到符号构造函数 VerifyApp()”的原因与 Java 和 Scala 之间的差异有关。
确实,反编译的 Java 代码如果重新编译,将有一个默认的 public 零参数构造函数。这是 Java 编译器的一个特性:如果没有声明构造函数,它将把这个默认构造函数插入到编译的字节码中。请注意,如果编译的字节码中不存在默认构造函数,则字节码解释器本身不会在运行时插入默认构造函数。
在最初的 Scala 源代码中,它被定义为 singleton object。它看起来像这样:
object VerifyApp {
def main(args: Array[String]): Unit =
???
}
我不知道实际的方法体是什么样的,因为它会被编译成一个名为 VerifyApp$
的 class,你可以在反编译的源代码中看到引用问题。
通常,Scala 编译器通过以下方式编译 object
定义:
- 创建一个 class,对象名称后跟
$
,包含对象上定义的方法的实例方法、私有构造函数和静态最终MODULE$
包含 class. 实例的字段
- 使用对象的普通名称创建一个 class,该对象包含调用该
MODULE$
实例上的匹配方法的静态转发器方法,以及 no 构造函数.
你可以使用javap
程序看到这个,例如:
javap -p -c com/company/VerifyApp.class com/company/VerifyApp$.class
你会得到类似这样的输出:
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
}
从 Java 访问这些对象的常用方法是使用静态转发器方法而不尝试实例化 class。在这种情况下,VerifyApp.main(args)
(如您所见)。
在极少数情况下,您确实需要直接访问实例(例如,当它实现了您需要其实例的接口时),您需要使用难看的 VerifyApp$.MODULE$
引用。它也可以分配给 Java class 或接口的另一个 static final
字段以提供更具可读性的名称。