從靜態上下文呼叫 getClass()
1. 概述
在 Java 中,了解如何在靜態和非靜態上下文中呼叫方法至關重要,尤其是在使用getClass()等方法時。
我們可能遇到的一個常見問題是嘗試從靜態上下文呼叫getClass()方法,這將導致編譯錯誤。
在本教程中,我們將探討為什麼會發生這種情況以及如何正確處理它。
2.問題介紹
getClass()方法繼承自Object類,並傳回所呼叫物件的執行時間Class 。當我們使用getClass(), Java 為我們提供了一個代表該物件執行時間類型的Class物件的實例。
接下來,我們來看一個例子:
class Player {
private String name;
private int age;
public Player(String name, int age) {
this.name = name;
this.age = age;
}
public Class<?> currentClass() {
return getClass();
}
}
在此範例中, Player是一個非常簡單的類別。 currentClass()方法是一個實例方法,它允許我們從Player實例取得Class<Player>物件:
Player kai = new Player("Kai", 25);
assertSame(Player.class, kai.currentClass());
有時,我們會想從靜態方法中取得目前的Class物件。所以,我們可能會想出這樣的方法:
class Player {
// ... unrelated code omitted
public static Class<?> getClassInStatic() {
return getClass();
}
}
但是,此程式碼無法編譯:
java: non-static method getClass() cannot be referenced from a static context
接下來,讓我們了解為什麼會發生這種情況,並探索解決此問題的不同方法。
3. 為什麼我們不能在靜態上下文中呼叫getClass() ?
為了找出這個問題的原因,讓我們快速了解Java中的靜態和非靜態上下文。
靜態方法或變數屬於類別而不是任何特定的類別實例。這意味著無需建立類別的實例即可存取靜態成員。
另一方面,非靜態成員(例如實例方法和變數)與類別的各個實例相關聯。如果不創建類別的對象,我們就無法存取它們。
現在,讓我們檢查一下編譯器錯誤發生的原因。
首先, getClass()是一個實例方法:
public class Object {
// ... unrelated code omitted
public final native Class<?> getClass();
}
我們的getClassInStatic()是一個靜態方法,我們在沒有Player實例的情況下呼叫它。然而,在這個方法中,我們呼叫了getClass().編譯器會引發錯誤,因為**getClass()需要類別的實例來決定執行時間類型,但靜態方法沒有任何與其關聯的實例。**
接下來我們來看看如何解決這個問題。
4. 使用類別文字
在靜態上下文中檢索目前Class物件最直接的方法是使用類別文字 ( ClassName.class ):
class Player {
// ... unrelated code omitted
public static Class<?> currentClassByClassLiteral() {
return Player.class;
}
}
正如我們所看到的,在currentClassByClassLiteral()方法中,我們使用其類別文字 Player.class 傳回Player類別的Class物件Player.class.
由於currentClassByClassLiteral()是靜態方法,因此它不依賴實例。我們可以從類別中調用它:
assertSame(Player.class, Player.currentClassByClassLiteral());
如測試所示,使用類別文字在靜態上下文中取得類別類型非常簡單。但是,我們必須在編譯時知道類別名稱。如果我們需要在運行時動態確定類別類型,這將無濟於事。
接下來我們來看看如何動態取得靜態上下文中的Class物件。
5. 使用MethodHandles
MethodHandle是在 Java 7 中引入的,主要用於方法句柄和動態呼叫的上下文。
MethodHandles.Lookup類別提供了各種類似反射的功能,並且**lookupClass()方法傳回執行lookup()方法的Class物件**。
接下來,讓我們在Player類別中建立一個靜態方法,以使用以下方法傳回Class物件:
class Player {
// ... unrelated codes omitted
public static Class<?> currentClassByMethodHandles() {
return MethodHandles.lookup().lookupClass();
}
}
當我們呼叫這個方法時,我們得到了預期的Player.class物件:
assertSame(Player.class, Player.currentClassByMethodHandles());
lookupClass()方法動態傳回執行lookup()呼叫類別。換句話說, lookupClass()方法傳回建立MethodHandles.Lookup實例的類別。當編寫需要在運行時反思性檢查當前類別的動態程式碼時,這非常有用。
此外,值得注意的是,由於它涉及動態查找和類似反射的機制,因此它通常比類文字方法慢。
六、結論
從靜態上下文呼叫getClass()會導致 Java 中出現編譯錯誤。在本文中,我們討論了此錯誤的根本原因。
此外,我們還探索了兩種解決方案來避免此問題並從靜態上下文中取得預期的Class物件。使用類文字既簡單又有效率。如果我們在編譯時擁有類信息,這是最好的選擇。但是,如果我們需要在運行時處理動態調用, MethodHandles方法是一個有用的替代方案。
與往常一樣,範例的完整原始程式碼可 在 GitHub 上取得。