2011-02-14 45 views
3

我有很多代碼調用Foo上的靜態方法,如「Foo.method()」。我有兩個不同的Foo實現,並且想根據具體情況使用其中一個或另一個。在僞代碼:是否有可能使用相同的名稱在Java中加載兩個不同的類中的一個?

文件Foo1.java

class Foo1 implements Foo { 
    public static int method() { 
    return 0; 
    } 
} 

文件Foo2.java

class Foo2 implements Foo { 
    public static int method() { 
    return 1; 
    } 
} 

文件Main.java

if(shouldLoadFoo1()) { 
    Foo = loadClass("Foo1"); 
} else { 
    Foo = loadClass("Foo2"); 
} 

這可能與Java的元編程?我無法把所有的動態類加載文檔包裹起來。如果沒有,做我想做的事情的最佳方式是什麼?

+0

爲什麼需要動態加載類,而不是僅創建Foo1和Foo2的單獨實例? – 2011-02-14 23:35:30

+0

我猜他的問題在於Foo1和Foo2的方法是靜態的。這有點奇怪,對同一個操作有不同的實現通常意味着他不應該使用靜態方法,但很難判斷這個通用的例子。史蒂夫,這些課程實際上做了什麼?如果他們的合同是一樣的,爲什麼你不使用接口? – PaoloVictor 2011-02-14 23:42:29

+0

你確定`method()是靜態的嗎?它從提供的代碼看起來並不那樣。 – CurtainDog 2011-02-14 23:54:17

回答

3

本質上你有兩個具有相同接口但不同實現的類,使用接口做它會更好嗎?

在你的主類,這取決於你將使用適當的實例構造你的類的情況。

FooInterface foo; 
MainClass (FooInteface foo, other fields) { 
    this.foo = foo; 
} 


.... 

然後就從他們使用foo。

另一種方法是用AspectJ,在每次通話Foo.method定義一個切入點,在爲切入點的建議有你if (shouldLoadFoo1()) { Foo1.method()}等。

2

可以使用工廠模式來做到這一點。

static Foo makeMeAFoo() 
{ 
    final Foo foo; 
    if(shouldLoadFoo1()) { 
    foo = new Foo1(); 
    } else { 
    foo = new Foo2(); 
    } 
    return foo; 
} 

我在想你在問什麼。儘管我更喜歡hhafez的建議。 (注意我的答案現在是OBE b/c提問者將方法轉換爲靜態方法而不是實例方法,但其他答案者的語氣很好......通過顯式類加載解決這個問題僅僅是因爲你想要選擇特定的靜態方法是一個混亂。)

2

從語言學的角度來看,你寫的東西沒有任何意義。 Foo是一種類型,並且類型不是變量,不能出現在作業的LHS上。您不能將類型視爲Java中的值...該語言不允許它。

,你可以得到什麼,你正在嘗試做的最接近的是這樣的:

Class fooClass; 
if (loadFoo1) { 
    fooClass = Class.forName("some.pkg.Foo1"); 
} else { 
    fooClass = Class.forName("some.pkg.Foo2"); 
} 
Foo foo = (Foo) fooClass.newInstance(); // using the no-args constructor 

(我已經離開了異常處理...)

注意fooClass會是Class類的一個實例,它提供用於反射執行操作的運行時句柄。我們實際上並沒有分配類型。我們以有限的方式分配一個「表示」類型的對象。


無論其 ...如果你不這樣做需要使用動態加載,你不應該使用它。換句話說,如果你正試圖解決的根本問題是創建類的實例,可能靜態加載,那麼最好使用工廠模式;例如,請參閱@ andersoj的答案。


UPDATE

我只是想出了什麼你可能想在這裏做。也就是說,您正試圖找出一種方法來在不同的靜態方法(即Foo1.method()Foo2.method())之間進行選擇,而無需在進行調用的位置明確指定類。

同樣,你正在嘗試做的,根本不會在Java中工作:

  • 你不能聲明一個接口的靜態方法。
  • 您不能通過接口在實現類中調用靜態方法。
  • 靜態方法調用不在Java中「調度」。它們是靜態綁定的。

有一種方法可以使用反射來做類似這樣的事情;例如

Class fooClass; 

// Load one or other of the classes as above. 

Method m = fooClass.getDeclaredMethod("method"); 
Integer res = (Integer) m.invoke(null); 

(和以前一樣,我已經離開了異常處理)

再次,你會好得多這樣做,而不訴諸動態加載和思考。簡單的方法是在一些公用事業類來創建這樣一個輔助方法:

public static int method() { 
    return useFoo1 ? Foo1.method() : Foo2.method(); 
} 

更妙的是,這樣做的OO方式:在Foo接口作爲實例方法聲明method,創建注入了一單或例如Foo1Foo2,並依賴於多態性。

但帶走的是,有沒有辦法避免變化的所有地方method()被稱爲...如果你希望能夠在運行時Foo1.methodFoo2.method之間做出選擇在你的代碼的地方。

2

交換實現的典型方法是使用非靜態方法和多態,通常使用依賴注入來告訴依賴代碼使用的實現。

下一個乾淨的方式是單例模式,即宣佈:

public abstract class Foo { 
    protected abstract void doSomeMethod(); 

    // populated at startup using whatever logic you desire 
    public static Foo instance; 

    public static void someMethod() { 
     instance.doSomeMethod(); 
    } 
} 

解決你的問題是你要的是什麼,即對同一類兩種不同的類文件的真正哈克的方式,並在運行時決定使用哪一個。要做到這一點,您需要將項目分爲4個不同的jar文件:

  • loader.jar確定要使用的類路徑並構造實際應用程序的類加載器。 loader.jar中的類不能引用Foo。
  • foo1。罐子,其中包含富
  • foo2.jar一個包含實現Foo的
  • common.jar另一種實現方式,它包含一切

Loader.jar隨後將包含類似的引導方法:

void bootstrap() { 
    URL commonUrl = // path to common.jar 
    URL fooUrl; 
    if (shouldUseFoo1()) { 
     fooUrl = // path to Foo1.jar 
    } else { 
     fooUrl = // path fo Foo2.jar 
    } 
    URL[] urls = {fooUrl, commonUrl}; 
    ClassLoader loader = new UrlClassLoader(urls); 
    Class<?> mainClass = loader.loadClass("my.main"); 
    mainClass.newInstance(); // start the app by invoking a constructor 
} 
1

在你的例子中,你實際上並沒有兩個不同版本的Foo類,但是兩個不同的接口Foo實現,在大多數情況下都很好。 (它們甚至可以彼此平行存在)。

可以加載多個同名的類,但它們必須由不同的類加載器加載。這也意味着你不能有第三個類通過名字引用它,然後使用其中一個或另一個(沒有第三個類也在兩個類加載器上)。

有時,對於使用不同配置的類(具有相同的外部接口),可能是明智的做法(例如「在客戶端」/「在服務器端」,當某些常見類在兩個模塊都依賴於它),並且在極少數情況下,您將同時在同一個虛擬機中使用兩個模塊 - 但在大多數情況下,最好使用「一個接口和多個實現類」方法。

2

我不確定我完全理解這裏的問題(我看到很多人有這個問題),但讓我試着幫忙。 如果您的問題只是使用適當的函數method(),您可以創建一個實用程序函數,根據給定類的實例將調用適當的方法,例如,

否則,我同意斯蒂芬C:「好吧,看我的答案那麼這就是你很可能在Java中得到最接近的。」

相關問題