我收到了一個 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()
- 位置:com.company.VerifyApp 類
- 請注意,此位置是正確的
- 從反編譯代碼來看,VerifyApp 是一個沒有建構式的最終類(因此應該有一個默認的 0-arg 建構式)
- 我正在使用 IntelliJ 并已激活“包含具有“提供”范圍的依賴項”
- 這不是一個 Maven 專案,我只是將 jar 作為外部庫加載
- 編輯時,IntelliJ 不顯示錯誤或警告
- 我檢查了大量具有相同主題的 Stackoverflow 帖子,但找不到任何有用的資訊
我想知道可能是什么問題?
編輯:IntelliJ 警告我我正在實體化一個實用程式類,這很可能是一個錯誤。由于我是 Java 新手,所以我用 Google 搜索了一下,發現也許我可以直接呼叫VerifyApp.main(args)而不實體化物件。這確實通過了構建,但我得到了一個運行時錯誤:
NoClassDefFoundError,由 ClassNotFoundException 引起
現在我沒戲了。
uj5u.com熱心網友回復:
回答原始問題,錯誤“找不到符號建構式 VerifyApp()”的原因與 Java 和 Scala 之間的差異有關。
確實,反編譯的 Java 代碼如果重新編譯,將有一個默認的、公共的、零引數的建構式。這是 Java 編譯器的一個特性:如果沒有宣告建構式,它會將這個默認建構式插入到編譯后的位元組碼中。請注意,如果編譯的位元組碼中不存在默認建構式,則位元組碼解釋器本身不會在運行時插入默認建構式。
在原始的 Scala 源代碼中,這被定義為單例物件。它看起來像這樣:
object VerifyApp {
def main(args: Array[String]): Unit =
???
}
我不知道實際的方法體是什么樣的,因為它會被編譯成一個名為 的類VerifyApp$,您可以在問題的反編譯源代碼中看到它的參考。
通常,Scala 編譯器通過以下方式編譯object定義:
- 創建一個類,其后跟物件的名稱
$,包含物件上定義的方法的實體方法、私有建構式和包含MODULE$該類實體的靜態最終欄位。 - 使用物件的純名稱創建一個類,該物件包含呼叫該
MODULE$實體上的匹配方法的靜態轉發器方法,并且沒有建構式。
您可以通過使用該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 訪問這些物件的常用方法是使用靜態轉發器方法而不嘗試實體化類。在這種情況下,VerifyApp.main(args)(如您所見)。
在您確實需要直接訪問實體的極少數情況下(例如,當它實作您需要實體的介面時),您需要使用難看的VerifyApp$.MODULE$參考。它也可以分配給static finalJava 類或介面的另一個欄位,以提供更易讀的名稱。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/414951.html
標籤:
