解決“無法實例化類型”編譯錯誤
1. 概述
在 Java 中,某些類型以藍圖的形式存在,而不是具體的實作。如果我們嘗試使用new關鍵字實例化這些類型,編譯器會拋出錯誤「無法實例化該類型」。
這種編譯時錯誤是一種安全機制,確保我們遵循 Java 的型別系統和抽象原則。理解 Java 為什麼阻止直接實例化某些類型,有助於我們編寫更健壯的程式碼。
在本教程中,我們將學習導致此錯誤的原因以及如何解決interfaces 、 abstract classes和enums此錯誤。我們還將了解 Java 17 及更高版本中的密封類別如何對類型層次結構添加更多約束。
2. 理解錯誤
「無法實例化該類型」錯誤表示我們試圖實例化 Java 不允許直接建立的類型。這種限制是設計使然,旨在維護適當的抽象和類型安全。
當我們寫如下程式碼時,編譯器會阻止我們:
List<String> list = new List<>(); // Cannot instantiate the type List
Java 強制執行此限制,因為這些類型故意是不完整的,或具有特殊的實例化規則,阻止它們與new關鍵字一起使用。
3. 常見原因
讓我們來分析一下出現這種錯誤的三種最常見情況。
3.1. 嘗試實例化介面
介面定義了類別必須實現的契約,但它們本身並不包含任何實作。因此,我們不能直接建立介面的實例:
// This causes a compilation error
Map<String, Integer> scores = new Map<>();
Set<String> names = new Set<>();
編譯器會拒絕這段程式碼,因為Map和Set是介面。它們規定了實作類別必須提供哪些方法,但並不提供實際的實作。
自 Java 8 起,介面可以包含預設方法和靜態方法,但這並不意味著它們可以直接實例化,因為介面不能保存狀態,而且它們仍然可以用於多重繼承。這就是為什麼編譯後的 JVM 工件會被標記ACC_INTERFACE標誌的原因。因此,我們不能直接建立介面的實例。
3.2. 嘗試實例化抽象類
抽象類別是部分實現的類,不能單獨實例化。它們通常包含子類別必須實作的抽象方法:
public abstract class DataExporter {
protected String filename;
public abstract void export(List<String> data);
public String getFilename() {
return filename;
}
}
// This causes a compilation error
DataExporter exporter = new DataExporter(); // Cannot instantiate the type DataExporter
由於DataExporter類別有一個抽象方法export() ,Java 無法建立它的完整實例。該類別有意設計為不完整,需要一個具體的子類別來提供缺失的實作。
3.3. 嘗試實例化枚舉
枚舉類型在編譯時定義了一組固定的實例,我們不能使用new關鍵字建立新的實例:
public enum Status {
ACTIVE, INACTIVE, PENDING
}
// This causes a compilation error
Status status = new Status(); // Cannot instantiate the type Status
enum類別載入時,JVM 會自動建立枚舉實例。建構子隱式為private ,防止外部實例化。
4. 如何修復此錯誤?
解決方案取決於我們要實例化的類型。
對於接口,我們使用具體的實作類別:
List<String> list = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
Set<String> names = new HashSet<>();
對於抽象類,我們需要找到或編寫一個具體的子類別來實作所有抽象方法:
public class CsvExporter extends DataExporter {
@Override
public void export(List<String> data) {
// Write data to CSV file
}
}
DataExporter exporter = new CsvExporter();
exporter.export(data);
最後,我們從不實例化enums ;相反,我們只是引用預定義的常數:
Status status = Status.ACTIVE;
5. 密封類別和實例化約束(Java 17+)
Java 17 引入了密封類,為類型層次結構增加了一層約束。密封類別或介面使用permits子句限制哪些類別可以擴充或實作它:
public sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}
透過此聲明,只有Circle 、 Rectangle和Triangle可以實作Shape 。與常規接口一樣,我們仍然不能直接實例化密封接口,但現在允許的實作集合已得到明確控制:
// This still causes a compilation error
Shape shape = new Shape();
// We must use one of the permitted implementations
Shape shape = new Circle(5.0);
密封類別的工作方式類似。如果我們聲明一個抽象密封類,則只有允許的子類才能擴展它:
public abstract sealed class Payment permits CreditCardPayment, BankTransfer {
protected double amount;
}
// This still causes a compilation error
Payment payment = new Payment();
// We must use a permitted subclass
Payment payment = new CreditCardPayment(100.0);
密封類型在模式匹配中特別有用,因為編譯器知道完整的子類型集,並且可以驗證switch表達式中的窮盡性。
6. 結論
在本文中,我們探討了 Java 中的「無法實例化類型」編譯錯誤。
首先,我們探討了Java為何禁止直接實例化某些類型以維護抽象性和類型安全性。其次,我們確定了三個常見原因:嘗試實例化interfaces 、 abstract classes或enums 。
我們還學習如何透過使用介面的具體實作、建立抽象類別的子類別以及直接引用枚舉常數來解決每個案例。
和往常一樣,程式碼可以在 GitHub 上找到。