什麼是錯誤:「無法從靜態上下文引用非靜態方法」?
1. 概述
當我們使用 Java 時,經常會遇到需要更深入了解該語言的複雜性的問題。一個常見的難題是錯誤訊息:“非靜態方法…無法從靜態上下文中引用。”這個錯誤對於初學者來說可能令人望而生畏,甚至可能讓經驗豐富的程式設計師感到困惑。
在本教程中,我們將深入研究此錯誤背後的原因並探索解決方法。
2.問題介紹
像往常一樣,讓我們透過一個例子來快速理解這個問題。假設我們有ToolBox
類別:
class ToolBox {
private String concat(String str1, String str2) {
return str1 + str2;
}
static String joinTwoStrings(String str1, String str2) {
return concat(str1, str2); //<-- compilation error
}
}
ToolBox
類別具有concat()
方法。我們不希望每個人都呼叫它,因此我們將其聲明為private
方法。此外,我們還有一個靜態方法joinTwoStrings()
,它在內部呼叫concat()
方法。
但是,如果我們編譯它,就會出現編譯錯誤:
java: non-static method concat(java.lang.String,java.lang.String) cannot be referenced from a static context
接下來我們就來了解這個錯誤訊息的含義並看看如何解決。
3. 錯誤是什麼意思?
在解決非靜態方法問題之前,讓我們先來了解 Java 中靜態上下文的概念。
在Java中,關鍵字「 static
」用於聲明屬於類別而不是實例的元素。靜態成員在類別的所有實例之間共享,並且無需建立該類別的物件即可存取。
然而,另一方面,非靜態方法與類別的實例相關聯,並且在不建立物件的情況下無法呼叫。它們可以依賴物件的特定狀態,並且它們的行為可能會根據實例變數的值而變化。
當嘗試從靜態上下文呼叫非靜態方法時,會出現編譯錯誤「非靜態方法…無法從靜態上下文引用」。此靜態上下文可以是靜態方法、靜態區塊或永遠是靜態的main()
方法。
現在我們了解了問題發生的原因,讓我們看看如何解決它。
4. 解決問題
我們了解到,如果不建立實例,則無法呼叫非靜態成員。然後,根據要求,我們有幾種方法來解決問題。
接下來,讓我們仔細看看它們。
4.1.從靜態上下文呼叫靜態方法
第一個解決方案是將實例方法變成靜態方法。如果我們已經完成了該轉換,那麼從靜態上下文中呼叫它就不會有任何問題:
class ToolBox {
private static String concatStatic(String str1, String str2) {
return str1 + str2;
}
static String joinTwoStrings(String str1, String str2) {
return concatStatic(str1, str2);
}
}
正如我們在上面的程式碼中看到的,為了更容易發現我們所做的更改,我們使用了新的方法名稱concatStatic
。此外,我們透過新增static
關鍵字使其成為靜態方法。
現在,如果我們呼叫靜態joinTwoStrings()
方法,我們會得到預期的結果:
assertEquals("ab", ToolBox.joinTwoStrings("a", "b"));
4.2.建立實例並呼叫實例方法
有時,需求不允許我們將實例方法變更為靜態方法。這種情況下,我們可以重構靜態方法,先建立實例,然後呼叫實例方法:
class ToolBox {
private String concat(String str1, String str2) {
return str1 + str2;
}
static String creatingInstanceJoinTwoStrings(String str1, String str2) {
ToolBox toolBox = new ToolBox();
return toolBox.concat(str1, str2);
}
}
現在,如果我們呼叫靜態的creatingInstanceJoinTwoStrings()
方法,它就可以正常運作:
assertEquals("ab", ToolBox.creatingInstanceJoinTwoStrings("a", "b"));
或者,我們可以考慮該類別中的creatingInstanceJoinTwoStrings()
方法是否必須是靜態的。如果沒有,我們也可以將靜態方法轉換為常規實例方法:
class ToolBox {
private String concat(String str1, String str2) {
return str1 + str2;
}
String instanceJoinTwoStrings(String str1, String str2) {
return concat(str1, str2);
}
}
透過此修復, instanceJoinTwoStrings()
方法不再是靜態的。因此,它可以直接呼叫私有的concrete()
實例方法。
當然,當我們使用instanceJoinTwoStrings()
時,我們必須先建立一個ToolBox
物件:
ToolBox toolBox = new ToolBox();
assertEquals("ab", toolBox.instanceJoinTwoStrings("a", "b"));
5. 靜態方法可以被實例呼叫嗎?
我們知道我們不能從靜態上下文中引用非靜態成員。有人可能會問,我們可以在實例方法中呼叫靜態方法嗎?
接下來我們來測試一下:
class ToolBox {
private static String concatStatic(String str1, String str2) {
return str1 + str2;
}
String instanceCallStaticJoinTwoStrings(String str1, String str2) {
return concatStatic(str1, str2);
}
}
正如我們在上面的程式碼中所看到的,實例方法instanceCallStaticJoinTwoStrings()
呼叫私有靜態方法concatStatic()
。
程式碼編譯。此外,如果我們測試它,它可以正常工作:
ToolBox toolBox = new ToolBox();
assertEquals("ab", toolBox.instanceCallStaticJoinTwoStrings("a", "b"));
所以,問題的答案是肯定的。
在Java中,允許從實例方法呼叫靜態方法。這是因為靜態成員不會綁定到特定實例。相反,它們與類別本身相關聯,並且可以使用類別名稱來呼叫。在我們的程式碼中,我們呼叫concatStatic(str1,
str2) 時沒有使用類別名稱「 ToolBox.concatStatic(str1, str2)”
,因為我們已經在ToolBox
類別中了。
六,結論
在本文中,我們探討了編譯錯誤“無法從靜態上下文引用非靜態方法”,深入研究其原因並檢查各種解決方案來解決和修復此問題。
與往常一樣,範例的完整原始程式碼可在 GitHub 上取得。