2012-08-13 43 views
6

我經常發現我想要做這樣的事情:爪哇 - 替代迫使子類有一個靜態方法

class Foo{ 
public static abstract String getParam(); 
} 

要強制美孚的一個子類返回的參數。

我知道你不能做到這一點,我知道你爲什麼不能做到這一點,但共同的選擇:

class Foo{ 
public abstract String getParam(); 
} 

是不能令人滿意的,因爲它需要你有一個實例,它是不是有益的,如果你只想知道參數的值,並且實例化類是很昂貴的。

我很想知道人們如何避免使用「恆定接口」反模式。

編輯:我會添加一些更詳細的關於我的具體問題,但是這僅僅是當我想要做這樣的事情有幾個人從過去的當前時間。

我的子類是所有數據處理器和超類定義它們之間的通用代碼,使它們能夠獲取數據,分析它,並把它放在它需要去。 每個處理器都需要一些保存在SQL數據庫中的參數。每個處理器應能夠提供它需要的參數列表和默認值,以便通過檢查每個處理器類型的必需參數來驗證配置數據庫或將其初始化爲默認值。 讓它在處理器的構造函數中執行是不可接受的,因爲每個類只需要爲每個對象實例一次而不是每個對象實例一次,並且應該在系統啓動時完成,否則每個類的實例可能還不需要。

+3

如果您已經調用了'Class.getName()',那麼爲什麼還需要獲取名稱? – 2012-08-13 16:34:17

+3

這是一個例子。我可以說得到gigawidgets – 2012-08-13 16:39:07

+0

有趣的問題。你有一個方便的具體例子嗎?這可能有助於挑起一些想法。斯卡拉有一個很好的方式來處理這個問題 - 它是一個對象和一個類的區別 - 可能不會幫助你很多。 – jeff 2012-08-13 17:05:17

回答

4

在我看來,你想用錯誤的工具解決錯誤的問題。 如果定義(真的不能說,繼承)的靜態方法,你仍然無法無痛調用它所有的子類(要調用在編譯時不知道一個類中的靜態方法是通過反射或字節碼操作)。

如果想法是有一組行爲,爲什麼不使用全部實現相同接口的實例?沒有特定狀態的實例在內存和構建時間方面便宜,如果沒有狀態,則可以爲所有呼叫者共享一個實例(flyweight模式)。

如果你只需要使用類,你可以建立/使用任何的元數據功能,你喜歡的,最基本的(手)的實現是使用一個Map,其中類對象是關鍵情侶元。如果這適合你的問題取決於你的問題,你沒有詳細描述。

編輯:(結構化)元數據將數據與(這只是一種風味,但可能是更常見的)相關聯。註釋可以用作非常簡單的元數據工具(用參數註釋該類)。有許多其他的方法(以及實現目標),複雜的一面是框架,它們基本上提供了設計到UML模型中用於在運行時訪問的所有信息。

但是你所描述的(數據庫中的處理器和參數)就是我爲「行爲集合」命名的。並且參數「參數需要每個類加載一次」是沒有意義的,它完全忽略了可以用來解決這個問題而不需要任何'靜態'的成語。即,flyweight模式(僅用於一次實例)和懶惰初始化(僅用於一次工作)。根據需要與工廠結合使用。

+0

你能否更詳細地描述你的最後一段。我已經爲這個問題添加了更多細節。我想避免集中保存元數據, – 2012-08-14 09:03:08

+0

@NickLong您似乎已經堅定了'靜態'方法。你需要調整你的心態。擁有一個類的實例打開了繼承和多態的所有優點。認爲這樣一個funtcion持有者實例被浪費 - 每個實例〜16個字節不會讓你的應用成爲一個吃巨獸的內存。 – Durandal 2012-08-14 13:14:55

+0

您的數據處理器+參數*示例數據處理器*工廠*會對已初始化的實例進行處理。另外請注意,*靜態*比*更集中*(後者至少可以在運行時在需要時更改)。靜態意味着自動*編譯時間依賴性*(爲了避免你*有*動態變化,至少部分原因)。 – Durandal 2012-08-14 13:18:38

8

,你可以在這裏做一個靜態的情況下,最佳是類似以下內容之一:

一個。你有沒有專門尋找一種方法,但沒有任何合同的一部分(因此你不能強制任何人來實現),並尋求在運行時:

public static String getParam() { ... }; 
try { 
    Method m = clazz.getDeclaredMethod("getParam"); 
    String param = (String) m.invoke(null); 
} 
catch (NoSuchMethodException e) { 
    // handle this error 
} 

灣使用一個註釋,它遇到了同樣的問題,因爲你不能強迫人們把它放在他們的類上。

@Target({TYPE}) 
@Retention(RUNTIME) 
public @interface Param { 
    String value() default ""; 
} 

@Param("foo") 
public class MyClass { ... } 


public static String getParam(Class<?> clazz) { 
    if (clazz.isAnnotationPresent(Param.class)) { 
     return clazz.getAnnotation(Param.class).value(); 
    } 
    else { 
     // what to do if there is no annotation 
    } 
} 
+0

註釋的好處。 – Matthieu 2016-09-11 21:20:49

4

我同意 - 我覺得這是Java的限制。當然,他們已經提出了不允許繼承靜態方法的優點,所以我明白了,但事實是,我遇到了這種情況,這將是有用的。考慮這種情況:

我有一個父類Condition類,並且對於它的每個子類,我想要一個getName()方法來聲明類的名稱。子類的名稱不會是Java的類名,而是Web前端用於JSON目的的一些小寫文本字符串。 getName()方法不會更改每個實例,因此將其設置爲靜態是安全的。然而,Condition類的一些子類將不被允許具有無參數構造函數 - 其中一些我需要在實例化時定義一些參數。

我使用Reflections庫在運行時獲取包中的所有類。現在,我需要一個包含在該包中的每個Condition類的所有名稱的列表,因此我可以將它返回到Web前端以進行JavaScript解析。我會通過實例化每個類的努力,但正如我所說的,它們並不都具有無參數構造函數。如果某些參數沒有正確定義,我設計了子類的構造函數來拋出IllegalArgumentException,所以我不能只傳遞null參數。這就是爲什麼我想要getName()方法是靜態的,但是對於所有子類都是必需的。

我目前的解決方法是執行以下操作:在Condition類(這是抽象的),我已經定義了一個方法:

public String getName() { 
    throw new IllegalArugmentException ("Child class did not declare an overridden getName() method using a static getConditionName() method. This must be done in order for the class to be registerred with Condition.getAllConditions()"); 
} 

因此,在每個子類,我簡單地定義:

@Override 
public String getName() { 
    return getConditionName(); 
} 

然後我爲每個定義了一個靜態的getConditionName()方法。這並不是強迫每個子類來這樣做,但我這樣做的方式是,如果getName()被無意中調用,程序員將被指示如何解決問題。

1

我一次又一次地遇到同樣的問題,我很難理解爲什麼Java 8更願意實現lambda而不是那個。無論如何,如果你的子類只實現檢索一些參數並執行相當簡單的任務,你可以使用enumerations,因爲它們在Java中非常強大:基本上可以認爲它是一組固定的接口實例。他們可以擁有成員,方法等等。他們不能被實例化(因爲他們是「實例化」的)。

public enum Processor { 
    PROC_IMAGE { 
     @Override 
     public String getParam() { 
      return "image"; 
     } 
    }, 

    PROC_TEXT { 
     @Override 
     public String getParam() { 
      return "text"; 
     } 
    } 
    ; 

    public abstract String getParam(); 

    public boolean doProcessing() { 
     System.out.println(getParam()); 
    } 
} 

的好處是,你可以通過調用Processor.values()得到所有「實例」:

for (Processor p : Processorvalues()) { 
    System.out.println(String.format("Param %s: %s", p.name(), p.getParam())); 
    p.doProcessing(); 
} 

如果處理比較複雜,你可以在在enum實例化其他類做方法:

@Override 
public String getParam() { 
    return new LookForParam("text").getParam(); 
} 

然後,您可以使用任何可以想到的新處理器來豐富枚舉。

不好的一面是,如果其他人想要創建新的處理器,就不能使用它,因爲這意味着修改源文件。