![](/img/trans.png)
[英]Is there a way to tell which child class called a parent's function in java?
[英]Is it possible to see via which child class was a parent's static method called in Java?
先是一點背景。 我正在研究盡可能干凈簡潔地在Java中實現Ruby的ActiveRecord的可能性。 為此,我需要允許以下類型的方法調用:
Person person = Person.find("name", "Mike");
哪個會解決這樣的問題:
ActiveRecord.find(Person.class, "name", "Mike");
計划是讓Person擴展ActiveRecord,它將具有帶有兩個參數(列,值)的靜態find方法。 這個方法需要知道它是通過Person.find調用的,而不是像Car.find那樣調用另一個域類,並調用find(Class,String,Object)方法來執行實際操作。
我遇到的問題是找出通過哪個子類ActiveRecord調用靜態查找方法(兩個參數)。 以下是一個簡單的測試用例:
public class A {
public static void testMethod() {
// need to know whether A.testMethod(), B.testMethod(), or C.testMethod() was called
}
}
public class B extends A { }
public class C extends A { }
public class Runner {
public static void main(String[] args) {
A.testMethod();
B.testMethod();
C.testMethod();
}
}
到目前為止找到的解決方案是使用aspectJ進行加載時或編譯時編織。 這將涉及在A中的testMethod()上放置一個調用攔截器,並找出用於調用它的簽名。 我完全是為了加載時間編織,但設置它(通過VM args)的設置有點復雜。
有更簡單的解決方案嗎?
這在java中是否可行,或者需要在groovy / ruby / python中完成?
將ActiveRecord.find用於靜態加載和Person.save用於實例的方法總體上會更好嗎?
您不能在Java中覆蓋靜態方法,因此通過子類對靜態方法的任何調用都將在編譯時綁定到基類。 因此,在運行應用程序之前,對B.testMethod()的調用將綁定到A.testMethod。
由於您在運行時查找信息,因此通過普通的Java操作無法使用它。
正如其他人所指出的那樣,我認為這個問題在Java中是不可解決的。 靜態方法實際上並沒有像非靜態方法那樣繼承。 (對不起,如果我沒有正確使用這個術語。)
然而,在我看來,如果你願意稍微修改你的界面,有很多方法可以達到預期的效果。
最明顯的是使用父類進行調用。 寫作有什么問題
Person person=(Person)ActiveRecord.find(Person.class, "name", "Mike");
?
或者,您可以先創建記錄類型的實例,然后執行查找以填充它。就像
Person person=new Person();
person.find("name", "Mike");
那時你有一個Person對象,如果你需要從超類型的函數中知道它的類,你只需要做“this.getClass()”。
或者,您可以創建一個虛擬Person對象來進行調用,只是為了讓您在必要時執行getClass()。 然后你的發現看起來像:
Person dummyPerson=new Person();
Person realPerson=dummyPerson.find("name", "Mike");
順便說一句,在我看來,任何具有泛型ActiveRecord類的嘗試都意味着find的返回類型必須是ActiveRecord而不是特定的記錄類型,因此您可能必須將其轉換為正確的類型從電話中回來。 擊敗它的唯一方法是在每個記錄對象中顯式覆蓋查找。
我有很多次寫過一些通用的記錄處理代碼,但我總是避免為每種記錄類型創建Java對象,因為這總是會變成編寫一大堆代碼。 我更喜歡保持Record對象完全通用,並且具有字段名稱,索引,無論是內部數據和名稱。 如果我想從“bar”記錄中檢索“foo”字段,我的界面將如下所示:
Record bar=Record.get(key);
String foo=bar.get("foo");
而不是:
BarRecord bar=BarRecord.get(key);
String foo=bar.getFoo();
不是很漂亮,它限制了編譯時錯誤檢查,但實現的代碼更少。
你不會在Java中這樣做。 你可能會做更像的事情:
public interface Finder<T, RT, CT>
{
T find(RT colName, CT value);
}
public class PersonFinder
implements Finder<Person, String, String>
{
public Person find(String nameCol, String name)
{
// code to find a person
}
}
public class CarFinder
implements Finder<Car, String, int>
{
public Person find(String yearCol, int year)
{
// code to find a car
}
}
這是可能的,但它很昂貴。
如果你能找到一種只調用一次的方法,那么你就可以了。
您可以創建一個新的例外並查看第一幀,然后您就會知道是誰調用它。 問題是它不是高效的。
例如,通過這個答案 ,可以創建一個這樣的記錄器:
class MyClass {
private static final SomeLogger logger = SomeLogger.getLogger();
....
}
並讓該記錄器創建一個不同的實例,具體取決於誰調用它。
所以,以同樣的方式,你可以有類似的東西:
class A {
public static void myStatic() {
// find out who call it
String calledFrom = new RuntimeException()
.getStackTrace()[1].getClassName();
}
}
這適用於一次初始化。 但不是1000個電話。 雖然我不知道一個好的VM是否可以為你內聯。
我會選擇AspectJ路徑。
我的相關理論是,使用代碼生成策略為包含該方法的每個類創建一個委托。 你不能在Java中擁有相當多的隱藏代碼,只要你生成合理的東西,它可能不值得付出努力。 如果你真的想要隱藏它,你可以做類似......
public class Person extends PersonActiveRecord
{
}
//generated class, do not touch
public class PersonActiveRecord extends ActiveRecord
{
public Person find(Map params)
{
ActiveRecord.find(Person.class, params);
}
}
但它往往會過多地破壞你的繼承層次結構。 我說只是生成類並完成它。 不值得隱藏find方法。
你可以通過創建一個hackish構造函數非常手動地完成它。
A example = new B(B.class);
並讓超類構造函數存儲傳遞給它的類。
我不認為上面拋出的異常會起作用,但是如果你想在沒有創建異常的情況下做這樣的事情......
Thread.currentThread().getStackTrace()
使用元編程和javassist,您可以更順利地完成它。
我想你想在Java中實現ActiveRecord。 當我決定這樣做時,我遇到了同樣的問題。 這對Java來說很難,但我能夠克服它。 我最近在這里發布了名為ActiveJDBC的整個框架: http : //code.google.com/p/activejdbc/
如果有興趣,您可以查看來源,看看它是如何實現的。 查看Model.getClassName()方法。
這就是我解決從靜態方法獲取類名的方法。 第二個問題是實際上將所有靜態方法從超類移動到子類(畢竟這是一種笨重的繼承形式!)。 我使用Javassist。 這兩個解決方案允許我完全用Java實現ActiveRecord。 字節碼操作最初是在加載類時動態完成的,但我在Glassfish和Weblogic中遇到了一些類加載問題,並決定實現靜態字節碼操作。 這是通過http:activejdbc.googlecode.com/svn/trunk/activejdbc-instrumentation/ Maven插件完成的。
我希望這能為你的問題提供詳盡的答案。
請享用,
伊戈爾
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.