簡體   English   中英

是否有可能通過哪個子類看到父在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM