2010-04-22 106 views
16

首先,我讀了埃裏克森對"Why can’t I define a static method in a Java interface?"的有用回覆。這個問題不是關於「爲什麼」,而是關於「怎麼做?」。 有沒有辦法確保實現接口的類實現靜態方法?


編輯:我最初的例子不適合,但我會在下面留下它。

雖然我現在相信,在大多數情況下,我想要做的就是矯枉過正,有一個場景可能需要它:

我將再次參加​​例子。現在讓我們來看一個複雜的函數,如Bessel functions,其中查找表是合適的。這必須進行初始化,所以這兩個選項直接將參數傳遞給構造函數或提供init(double[] parameters)。後者的缺點是getValue(double x)必須檢查初始化每次呼叫(或ArrayIndexOutOfBoundsException必須被視爲初始化檢查),所以對時間要求嚴格的應用我寧願構造法:

interface ParametricFunction { 
    public double getValue(double x); 
} 

class BesselFunction implements ParametricFunction { 
    public BesselFunction(double[] parameters) { ... } 
    public double getValue(double x) { ... } 
} 

其中涉及另一個問題是接口中構造函數的不可能性。那將是一個很好的解決方案?我當然可以使用init(double[] parameters)的方法,但我提到了我的理由。
(編輯:OK,這裏實現接口的抽象類會做)

現在讓我們假設​​只允許某些參數,例如正整數。如何檢查傳遞給構造函數的參數的威力?拋出一個IllegalArgument - 將是一種可能性,但checkParametersValidity(double[] parameters)似乎更方便。但是檢查參數需要在施工前完成,因此它必須是一個靜態方法。這就是我真正想知道的方法,以確保實現接口的每個類都確定了這種靜態方法。

我知道這個例子是相當人爲的,不通過接口簡單地使用init方法的原因是有爭議的,我仍然想知道答案。如果你不喜歡它,請考慮一個學術問題。

(原來的例子)

所以基本上我想一個接口以提供通常的方法和例如一種getSimilarObject方法。對於(一個由)例如

public interface ParametricFunction { 
    /** @return f(x) using the parameters */ 
    static abstract public double getValue(double x, double[] parameters); 

    /** @return The function's name */ 
    static abstract public String getName(); 

    /** @return Whether the parameters are valid [added on edit] */ 
    static abstract public boolean checkParameters(double[] parameters); 
} 

然後

public class Parabola implements ParametricFunction { 
    /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */ 
    static public double getValue(double x, double[] parameters) { 
    return (parameters[2] + x*(parameters[1] + x*parameters[0])); 
    } 
    static public String getName() { return "Parabola"; } 
    // edit: 
    static public boolean checkParameters(double[] parameters) { 
    return (parameters.length==3); 
    } 
} 

因爲這在目前的Java標準是不允許的,什麼是最接近這個?

這個想法背後是將幾個​​s放在一個包中,並使用Reflection將它們全部列出,從而允許用戶選擇例如哪一個要繪製。很顯然,可以提供一個包含可用的數組的加載器類,但是每次實現新的加載器時,都必須記住在其中添加新的加載器。

編輯:一個例子來調用它是

public double evaluate(String fnName, double x, double parameters) throws (a lot) { 
    Class<ParametricFunction> c = (Class<ParametricFunction>) ClassLoader.getSystemClassLoader().loadClass(fnName); 
    Method m = c.getMethod("getValue", x, parameters); 
    return ((double) m.invoke(null)); 
} 

,並呼籲evaluate("Parabola", 1, new double[]{1,2,0});

+1

爲什麼getValue必須是靜態的?如果getValue不是靜態的,你將能夠完成你想要的。 – nos 2010-04-22 08:50:46

+0

然後,我不得不創建該類的一個實例。糾正我,如果我錯了,但關於它的目的,似乎沒有用。 – 2010-04-22 09:03:25

+2

你的設計出了什麼問題。這不是面向對象。參數[]應該是Parabola類的實例字段,在構造函數中設置和檢查,並在getValue()類中使用。 – 2010-04-22 09:17:52

回答

14

無法要求類來實現通過接口特定的靜態方法。用Java術語來說這沒有意義。接口強制實現接口的類中存在特定的非靜態方法;這就是他們所做的。

最簡單的方法是有一些工廠類生成其他的實例。是的,這意味着當你添加新的實例時,你必須記住保持這個工廠是最新的,但是當你做一個新的實現時你做的第一件事就是測試它(你測試它,是嗎?),我會很快回復這個問題!

+5

爲什麼它沒有意義?一個接口需求類不能提供一個'static public String getDescription()'?我不會把這與javadoc混淆,我的意思是一個圖形用戶界面,用戶選擇'ParametricFunction'來適應數據,並且可能想知道每個函數如果名字本身並不那麼直觀就會做什麼。然後考慮'ParametricFunction'是一個更復雜的事情,這樣實例化會消耗很多時間,特別是當提供一個帶有100個'ParametericFunction'的描述的列表時,最終只會得到一個。 – 2010-04-22 10:57:25

+1

它沒有任何意義,因爲您在類而不是實例上調用靜態方法。對於這兩個類沒有興趣共享完全相同的靜態方法名稱,因爲您不從接口調用此方法... 您希望getDescription()返回什麼?班級的描述?或者當前實例類的描述? – 2010-04-22 12:17:34

+0

一些對象系統通過使靜態方法成爲類對象上的方法來解決這個問題。 Java沒有這樣做,決定在這方面更接近C++。 – 2010-04-22 12:34:56

3

這背後的想法是把 幾個ParametricFunction在一個 包裝和使用反射來列出 所有這些,讓用戶挑選 例如哪一個要繪製。

這是要失敗的一個更基本的原因:反射提供沒有辦​​法列出一個包中的所有類(因爲「包中的所有類」不是一個定義良好,由於的靈活性類加載器機制)。

這種事情的現代解決方案是通過依賴注入框架使其成爲應用程序配置的一部分。

顯然,一個能夠提供包含的 可用ParametricFunction的數組裝載機 類,但 每一個新的實施 人們必須記得添加它在那裏, 太費時間。

那麼,隨着你的理念,每次實施一個新的,都被迫把它放到同一個包中。通過將其放入配置文件或加載器類(同樣的事情,真的),您可以刪除該限制。

+0

好吧,我可以做邪惡的事情,並使用文件系統列出軟件包的目錄... 對我來說,將所有'ParametricFunction's放入像'my.math.function.parametric'這樣的軟件包是非常合理的。但是一個配置文件還需要類加載器並提供一個擴展的東西,例如(我將立即編輯該問題)'static boolean checkParameters(...)'仍然需要我請求的機制,不是嗎? – 2010-04-22 08:54:59

+0

@Tobias:配置文件將具有特定的名稱並列出要加載的類的具體名稱 - 在那裏沒有問題。至於那個checkParameter()的東西 - 這是錯誤的。你從根本上濫用面向對象的概念。類意味着具有不同狀態的多個實例,而不是靜態方法的集合。 – 2010-04-22 09:15:37

+0

如果不是類,我可以使用什麼作爲靜態方法的集合?我自己的答案是否使事情至少好一點? – 2010-04-22 09:24:39

0

一個解決方案 - 使所有方法都是非靜態的,要求類必須具有默認構造函數。然後,您可以輕鬆實例化並調用所需的方法。

+1

@Ha:你的意思就像我在自己發佈的答案中一樣?我重新思考它,但對我來說,似乎沒有必要實例化一個類,其中有幾個實例的可能性沒有提供任何有用的東西。 – 2010-04-22 09:28:26

+1

是的,你失去了16個字節來存儲實例,但獲得了很多速度,因爲只有構造函數是通過反射調用的。 – 2010-04-22 10:23:41

+0

我已經看到這種模式在日食中使用。 Mylyn連接器插件提供了擴展一些抽象類的類,並且通過反射在mylyn內部的某個地方創建了連接器類的單個實例。你的情況也是如此 - 只有一個反射意識的單人課程將創建所有必要的實例並將其放入地圖中。 – 2010-04-22 10:32:39

0

你想要做什麼都不行......

你要定義一個接口I靜態方法,並有一些實現A和B這個接口的,用自己的實現在這些聲明的靜態方法接口I.

想象一下,如果你打電話給i.staticMethod(),計算機怎麼知道該怎麼辦?它會使用A還是B的實現?

在接口中聲明一個方法的興趣是使用多態,並且能夠爲不同的對象實現調用此方法......但對於靜態方法,由於您不從實例調用該方法(實際上是您可以但並非真的需要......)但使用ClassName.xxxMethod,絕對沒有興趣...

因此,您不必將這些靜態方法放在接口中......只需將它們放入實現並使用A.staticMethod()和B.staticMethod()調用它們(並且它們甚至不需要共享相同的方法名稱!)

我想知道你想如何調用你的靜態方法嗎?代碼顯示?

+0

我永遠不會想要調用I.staticMethod(),這就是爲什麼我把它標記爲抽象的原因。現在編輯的問題就是一個例子。 – 2010-04-22 09:37:16

+0

所以你會打電話給A.staticMethod或B.staticMethod ... 因此,你知道在編譯時你調用哪個類的方法,那麼爲什麼你需要有一個與靜態方法的公共接口? 如果你要調用對象實例的靜態方法(i.staticMethod(無論如何不可能),a.staticMethod或b.staticMethod),那麼你最好考慮使用非靜態方法... – 2010-04-22 10:16:23

+3

因爲我想要A和B來肯定地實現這個staticMethod *並確保其他人使用接口來創建新類也會這樣做。 – 2010-04-22 10:45:53

2

您對自己問題的回答可以進一步簡化。保持​​接口原樣,並改變Parabola爲實現​​單身:

public class Parabola implements ParametricFunction { 
    private static Parabola instance = new Parabola(); 

    private Parabola() {} 

    static public ParametricFunction getInstance() { 
    return instance; 
    } 

    public double getValue(double x, double[] parameters) { 
    return (parameters[2] + x*(parameters[1] + x*parameters[0])); 
    } 
    public String getName() { return "Parabola"; } 
    public boolean checkParameters(double[] parameters) { 
    return (parameters.length==3); 
    } 
} 

事實上,如果有是爲什麼拋物線需要有一個單獨的類沒有特別的原因,你可以擺脫靜態方法並賦予構造函數公共性。

創造Parabola實例的目的簡化您的應用程序

編輯響應低於你的問題:

您不能使用標準的Java構建迫使一個類來實現與給定簽名的靜態方法。作爲Java中的抽象靜態方法,有沒有這樣的東西

您可以通過編寫一個單獨的工具作爲構建的一部分運行並檢查源代碼或編譯代碼來檢查是否實現了靜態方法。但海事組織,這是不值得的努力。如果您編譯調用它的代碼,或者在運行時嘗試反射使用它,任何缺少的getInstance()都會顯示出來。在我看來,這應該足夠好。

另外,我想不出一個有說服力的理由,你爲什麼要需要這個類是單身人士;即爲什麼getInstance方法是必要的。

+0

+1絕對不像我的方式複雜,謝謝。但是仍然沒有辦法確保一個新的類,例如'Gaussian',也會實現'getInstance()'方法。 – 2010-04-22 09:52:04

+0

爲什麼只創建一個實例,實際上在他的應用程序中可能會有很多拋物線... Singleton應該用於工具箱,服務等,但沒有模型對象。 我真的認爲你只是缺乏面向對象的基礎知識! – 2010-04-22 12:21:38

+2

@Sebastien Lorber - 你在跟我說話嗎?你讀過我的全部答案嗎? – 2010-04-22 12:32:56

1

的原因是可讀性: 配合( 「拋物線」,xValues,fValues)與 配合(Parabola.getInstance(),xValues, fValues)與配合(新拋物線(), xValues, fValues)。爲什麼我要 有一個函數實例定義 完全由它的參數沒有 內部數據?

其實你缺少一些關於面向對象編程的基礎知識...

如果你定義一個對象拋物線,這個對象應該代表一個拋物線,而不是一個工具箱檢查參數都OK等..

你拋物線項目應包含的參數(X,Y ......),你可以通過他們與構造...

double x; 
double [] parameters; 
public Parabola(double x, double[] parameters) { 
    this.x = x; 
    this.parameters = parameters; 
} 

因此,你不應該在你的FUNC使用參數因爲參數重刑現在聲明爲類成員的屬性...

public double getValue() { 
    return (this.parameters[2] + x*(this.parameters[1] + x*this.parameters[0])); 
} 

然後就叫

parabolaInstance.getValue(); 
+0

我知道,但這需要一個合適的函數來每次調用'setParameters(parameters)'和'getValue(x)',而不是簡單的'getValue(x,parameters)'。另一方面,setParameters只會被調用以適應新的配置,並且爲每次評估傳遞參數都是一種浪費......我明白了。 +1 – 2010-04-22 10:17:34

+1

其實爲什麼不能做這樣的事情: 拋物線拋物線=新的拋物線(X,Y,Z) 配合(拋物線) 與 公共無效(或你想要的)配合(ParametricFunction pamFunc){ // yourimpl } – 2010-04-22 10:21:51

+0

+1確實如此。但是,我在回答Donal Fellows的回答時提到的問題呢? http://stackoverflow.com/questions/2689312/is-there-a-way-to-make-sure-classes-implementing-an-interface-implement-static-me/2689947#2689947 – 2010-04-22 11:04:40

4

爲什麼不嘗試的Java 5枚舉?即:

public enum ParametricFunctions implements ParametricFunction { 
    Parabola() { 
     /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */ 
     public double getValue(double x, double[] parameters) { 
      return (parameters[2] + x*(parameters[1] + x*parameters[0])); 
     } 

     public String getName() { return "Parabola"; } 

     public boolean checkParameters(double[] parameters) { 
      return (parameters.length==3); 
     } 
    }, 

    // other functions as enum members 
} 

有了這個,你可以看一下靜態函數類型很容易,並與編譯時的安全性,但仍允許接口類型在其他地方引用。您也可以在枚舉類型上放置一個方法,以允許按名稱查找函數。


用enum方式編輯文件大小問題。

在這種情況下,你可以定義每個功能,因爲它是自己的類,即:

public class Parabola implements ParametricFunction { 

    /** @return f(x) = parameters[0] * x² + parameters[1] * x + parameters[2] */ 
    public double getValue(double x, double[] parameters) { 
     return (parameters[2] + x*(parameters[1] + x*parameters[0])); 
    } 

    public String getName() { return "Parabola"; } 

    public boolean checkParameters(double[] parameters) { 
     return (parameters.length==3); 
    } 

}

然後你就可以有很多不同的,實現文件,並將它們組合成一個,小的,枚舉類功能可以靜態訪問的類。即:

public class ParametricFunctions { 
    public static final ParametricFunction parabola = new Parabola(), 
              bessel = new BesselFunction(), 
              // etc 
} 

這允許一個地方查找功能,實施保持分開。您也可以將它們添加到靜態集合中進行名稱查找。然後,您可以在您的功能保持可讀性爲另一則留言中提到:

import static ...ParametricFunctions.parabola; 
// etc 

public void someMethodCallingFit() { 
    fit(parabola, xValues, yValues); 
} 
+0

+1的想法。但不幸的是,如果我開始包含貝塞爾函數等,這可能會導致一個巨大的文件,這可能真的更好的在一個單獨的類... – 2010-04-22 10:48:42

+0

@Tobias:請參閱我的編輯地址是否涉及。 – Grundlefleck 2010-04-22 11:04:56

+0

您可以創建多個枚舉。它們僅用於確保實現是單例。 這種方法的一個自然發展是以某種方式在UI中的相同枚舉中分組函數。 – 2010-04-22 11:05:30

0

@Sebastien:爲什麼沒有兩個類興趣 分享確切 相同的靜態方法名?使用 反射這可能是 確保方法存在的唯一方法。我想 想getDescription()返回 這個類的描述。爲什麼 應該在不同的 實例上更改?這就是爲什麼我想這個 方法是靜態的,但在 執行一個類似於接口的方式,它實現了 。 - 托比亞斯Kienzler 3

正如我已經說過的聲明方法靜態意味着你可以直接從類調用它,並不需要類實例。因爲調用I.staticMethod()(正如已經解釋過的)沒有意義,所以你可以調用A.staticMethod1()和B.staticMethod2(),它們的名字根本不重要,因爲你從A調用它們或者B類,在編譯時已知!

如果您想讓getDescription返回相同的描述,而不管相關的ParametricFunction的實例,只需將ParametricFunction設置爲抽象類並直接在此類中實現靜態方法即可。然後你可以調用A,I或B.getDescription(); (即使是a,我或b ...)。但它仍然與在A和B中實現它並將其稱爲拋出A或B相同...

從實例調用靜態方法不是一種好的做法,也沒有興趣,因此應該調用A.meth (),或b.meth(),而不是a.meth()或b.meth()

因爲我想A和B來實現 是STATICMETHOD肯定,並 確保其他人用接口對於新班級, 也會這樣做。 - 托比亞斯Kienzler 4小時前

其實「別人」會normaly不叫a.meth()或b.meth(),因此如果他做C級,並要撥打的C.meth()他永遠無法做到這一點,因爲C.meth()沒有實現或不是靜態的...所以他會這樣做,或者C.meth()永遠不會被調用,那麼它也是無意義的developpers實施,將永遠不會被使用靜態函數...

我不知道我可以添加...

+0

我更新了這個問題,希望能夠澄清我的意圖。 – 2010-04-26 14:04:48

0

構造函數接口?呃?你想能夠調用Interface i = new Interface(double []參數)嗎?而電腦會在另一次選擇自己的執行?這與界面中的靜態一樣奇怪:D

正如您所說的,檢查參數應在施工前完成...... 但這並不意味着如果參數不正確,您不能在構建時引發異常。它只是一個你可以添加的安全性,這將確保構造的對象是連貫的。但是這樣的代碼不允許你繞過以前的驗證:在構造上引發異常會告訴你「嘿,你有一個bug!」雖然沒有驗證的參數會告訴你「hoho某人使用GUI試圖設置一個不好的值,我們會給他發一個錯誤信息......」

其實,因爲你需要驗證值和對象甚至沒有構建,爲什麼你絕對想要在模型對象上添加這個驗證?在驗證java類中,可以在任何地方完成Form/Gui /任何驗證...... 只需在另一個名爲ParametricFunctionValidationHelper的類中設置一個靜態(或不)方法,在其中添加方法和驗證實現。

public static boolean validateParametricFunction(String functionType, double[] parameters) { 
    if (functionType.equals("bessel")) return validateBessel(parameters); 
    if (functionType.equals("parabola")) return validateParabola(parameters); 
} 

不要緊,如何表示你的函數類型(我選擇字符串,因爲我想你的用戶界面,網站或GUI得到它......它可能是枚舉...

你甚至可以驗證了目標對象的構造後:

public static boolean validateParametricFunction(ParametricFunction pamFunc) { 
    if (pamFunc instanceOf BesselFunction) return validateBessel(pamFunc.getParameters); 
    ...... 
} 

你甚至可以把函數類的靜態驗證方法,然後你就會有: 公共靜態布爾validateParametricFunction(ParametricFunction pamFunc){ if(pamFunc instanceOf BesselFunction)返回BesselFunction.validateBessel(pamFunc.getParameters); (pamFunc instanceOf ParabolaFunction)返回ParabolaFunction.validateParabola(pamFunc.getParameters);如果(pamFunc instanceOf ParabolaFunction)返回ParabolaFunction.validateParabola(pamFunc.getParameters); }

是的,你將無法在界面中設置靜態方法,但無論如何,你會怎麼稱呼這樣的方法?

有了這樣

public static boolean validateParametricFunction(ParametricFunction pamFunc) { 
    return ParametricFunction.validate(pamFunc); 
} 

代碼???這是沒有意義的,因爲JVM根本無法知道要使用哪種靜態方法的實現,因爲您沒有從實例中調用靜態方法,而是從類中調用靜態方法!這只是在您直接在ParametricFunction類中實現validate方法時纔有意義,但無論如何,如果您做了這樣的事情,您必須執行與我之前向instanceOf顯示的完全相同的操作,因爲pamFunc的實例是唯一一個你必須選擇哪一種驗證你必須使用的項目...

這就是爲什麼你最好使用非靜態方法,並把它放在如接口:

public static boolean validateParametricFunction(ParametricFunction pamFunc) { 
    return pamFunc.validate(); 
} 

其實你應該做的是: - 檢索從GUI/Web界面的參數(字符串?) /任何東西 - 以良好格式分析字符串參數(字符串轉換爲int ...) - 使用驗證類驗證這些參數(是否爲靜態方法) - 如果沒有驗證 - >打印消息給用戶 - 其他構造對象 - 使用物品

我在接口中看不到任何需要靜態方法的地方...

相關問題