有没有办法确保实现接口的类实现静态方法?

首先,我阅读了erickson对“为什么我不能在Java接口中定义静态方法?”的有用回复。 。 这个问题不是关于“为什么”而是关于“如何呢?”。


编辑:我原来的例子不合适,但我会把它留在下面。

虽然我现在确信在大多数情况下我想做的事情是矫枉过正,但有一种情况可能需要它:

我将再次参加ParametricFunction示例。 现在让我们看一个复杂的函数,比如Bessel函数 ,其中查找表是合适的。 这必须初始化,因此两个选项是将参数直接传递给构造函数或提供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)方法,但我之所以提到我的原因。
(编辑:好的,这里实现接口的抽象类会这样做)

现在让我们假设ParametricFunction只允许某些参数,例如正整数。 如何检查传递给构造函数的参数的真实性? 抛出IllegalArgument checkParametersValidity(double[] parameters)是可能的,但checkParametersValidity(double[] parameters)似乎更方便。 但是在构造之前需要检查参数,因此它必须是静态方法。 这就是我真正想知道的方法,以确保实现ParametricFunction接口的每个类都确实定义了这个静态方法。

我知道这个例子是相当人为的,不仅仅是通过接口使用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标准中不允许这样做,最接近它的是什么?

这背后的想法是将一些ParametricFunction放在一个包中,并使用Reflection将它们全部列出,允许用户选择哪一个绘图。 显然,可以提供一个包含可用ParametricFunction数组的加载器类,但每次实现一个新的时,都必须记住在那里添加它。

编辑:调用它的一个例子是

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

并调用evaluate("Parabola", 1, new double[]{1,2,0});

不能要求类通过接口实现特定的静态方法。 它在Java术语中毫无意义。 接口强制在实现接口的类中存在特定的非静态方法; 这就是他们所做的。

最简单的方法是使用某种工厂类来生成其他工厂的实例。 是的,这确实意味着你必须记住在添加新实例时保持该工厂的最新状态,但是因为你做一个新实现时做的第一件事就是测试它(你测试它,是吗?)你’我会很快发现这个问题!

为什么不试试Java 5 enum? 即:

 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 } 

通过这种方式,您可以轻松查找静态函数类型,并且具有编译时安全性,但仍允许在其他地方引用接口类型。 您还可以在枚举类型上放置一个方法,以允许按名称查找函数。


使用枚举方式编辑文件大小的问题。

在这种情况下,您可以将每个函数定义为它自己的类,即:

 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 } 

这允许单个位置查找function,实现保持独立。 您还可以将它们添加到静态集合中以进行名称查找。 然后,您可以在其他注释中提到的函数中保持可读性:

 import static ...ParametricFunctions.parabola; // etc public void someMethodCallingFit() { fit(parabola, xValues, yValues); } 

这背后的想法是将一些ParametricFunction放在一个包中,并使用Reflection将它们全部列出,允许用户选择哪一个绘图。

由于更基本的原因,这将失败:reflection无法列出包中的所有类(因为“包中的所有类”不是一个定义良好的集合,因为类加载器机制的灵活性)。

这种事情的现代解决方案是通过dependency injection框架使其成为应用程序配置的一部分。

显然,人们可以提供一个包含可用ParametricFunction数组的加载器类,但每次实现一个新的时,必须记住在那里添加它。

好吧,根据您的概念,每次实施新的概念时,都会被迫将其放入同一个包中。 通过将其放入配置文件或加载器类(实际上是同样的事情),您可以删除该限制。

您可以进一步简化对自己问题的回答。 保持ParametricFunction接口不变,并将Parabola更改为实现ParametricFunction的单例:

 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需要成为单例类没有特别的原因,你可以摆脱静态方法和属性并使构造函数公开。

创建Parabola实例的目的简化您的应用程序

编辑以回答您的问题如下:

您不能使用标准Java构造来强制类实现具有给定签名的静态方法。 Java中没有抽象的静态方法。

您可以通过编写作为构建的一部分运行的单独工具来检查是否实现了静态方法,并检查源代码或编译代码。 但IMO,不值得努力。 如果编译调用它的代码,则会显示任何缺少的getInstance() ,如果您尝试reflection性地使用它,则会在运行时显示。 在我看来,这应该足够好了。

此外,我想不出一个令人信服的理由,为什么你需要这个class级成为一个单身人士; 即为什么getInstance方法是必要的。

原因是可读性:拟合(“抛物线”,x值,f值)与拟合(Parabola.getInstance(),xValues,fValues)对拟合(新抛物线(),x值,f值)。 为什么我想要一个完全没有内部数据的参数定义的函数实例?

实际上你缺少一些关于面向对象编程基础知识的东西……

如果你定义一个对象抛物线,这个对象应该代表一个抛物线,而不是一个工具箱来检查参数是否正确等…

你的抛物线项应该包含参数(x,y …),你可以用构造函数传递它们……

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

因此,您不应该在函数上使用参数,因为参数现在被声明为类成员属性…

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

然后打电话

 parabolaInstance.getValue(); 

一种解决方案 – 使所有方法都是非静态的,并要求类必须具有默认构造函数。 然后,您可以轻松地实例化它并调用您需要的方法。

你想做什么不行……

您希望在接口I中定义静态方法,并且具有此接口的一些实现A和B,以及它们在接口I中声明的这些静态方法的实现。

试想一下,如果你打电话给I.staticMethod(),电脑怎么会知道怎么做? 它会使用A还是B的实现?!!

在接口中声明方法的兴趣是使用多态并能够为不同的对象实现调用此方法…但对于静态方法,因为您不从实例调用该方法(实际上您可以但不是真的需要…)但是使用ClassName.xxxMethod,它绝对没有兴趣……

因此,您不必将这些静态方法放在接口中……只需将它们放在两个实现中并使用A.staticMethod()和B.staticMethod()调用它们(它们甚至不需要共享方法名称相同!)

我想知道你想如何调用静态方法,你是否展示了代码示例?

@Sebastien:为什么这两个类没有兴趣共享完全相同的静态方法名称? 使用reflection这可能是确保方法存在的唯一方法。 我想getDescription()返回类的描述。 为什么它会在不同的情况下改变? 这就是为什么我希望这种方法是静态的,并且以类似接口的方式强制执行它。 – Tobias Kienzler 3

正如我已经说过声明方法静态意味着你可以直接从类中调用它而不需要类实例。 因为没有理由调用I.staticMethod()(如已经解释过的),你可以调用A.staticMethod1()和B.staticMethod2(),因为你从A中调用它们,它们的名字根本不重要。 B类,编译时知道!

如果您希望getDescription返回相同的描述,无论相关的ParametricFunction实例如何,只需将ParametricFunction设为抽象类并直接在此类中实现静态方法。 然后你就可以调用A,I或B.getDescription(); (甚至是a,i或b ……)。 但它仍然与在A和B中实现它并且称它扔A或B相同……

从实例调用静态方法不是一个好习惯,没有兴趣,所以你应该调用A.meth()或B.meth()而不是a.meth()或b.meth()

因为我希望A和B确实实现staticMethod,并确保其他人使用Interface作为新类也会这样做。 – Tobias Kienzler 5小时前

实际上“别人”通常不会调用a.meth()或b.meth()因此如果他做了C级并且想要调用C.meth()他将永远无法做到这一点,因为C.meth ()没有实现或者不是静态的…所以他会这样做,或者C.meth()永远不会被调用,然后迫使开发人员实现永远不会被使用的静态函数也是没有意义的……

我不知道我能添加什么……

接口中的构造函数? 呃? 您是否希望能够调用Interface i = new Interface(double []参数)? 而计算机又会选择自己的实施吗? 这和界面中的静态一样奇怪:D

如你所说,检查参数应该在构造之前完成……但是这并不意味着如果参数不正确你就不能在构造时引发exception。 它只是一个你可以添加的安全性,它将确保构造的对象是连贯的。 但是这样的代码不允许你绕过先前的validation:在构造上提出exception将告诉你“嘿,你有一个bug!” 虽然没有validation参数只会告诉你“有人使用GUI试图设置一个坏的值,我们会给他发一个错误信息……”

实际上,由于您需要validation值,并且对象甚至没有构造,为什么您绝对想在模型对象上添加此validation? Form / Gui /可以在任何地方进行validation,在validationjava类中……只需在另一个名为ParametricFunctionValidationHelper的类中设置静态(或非)方法,在其中添加方法和validation实现。

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

无论如何表示你的functionType(我选择String因为我想你是从用户界面,web或gui获得它…它可能是Enum …

您甚至可以在构造对象后validation它:

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

您甚至可以在函数类中放置静态validation方法,然后您将拥有:public static boolean validateParametricFunction(ParametricFunction pamFunc){if(pamFunc instanceOf BesselFunction)return BesselFunction.validateBessel(pamFunc.getParameters); if(pamFunc instanceOf ParabolaFunction)返回ParabolaFunction.validateParabola(pamFunc.getParameters); }

是的,你将无法在界面中设置静态方法,但无论如何你会如何调用这种方法?

用代码就好

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

??? 这没有任何意义,因为JVM根本无法知道要使用静态方法的哪个实现,因为您不是从实例调用静态方法而是从类调用静态方法! 只有在参数化函数类中直接实现validate方法才有意义,但无论如何,如果你做了这样的事情,你必须完全按照我之前用instanceOf向你展示的那样做,因为pamFunc的实例是您必须选择的唯一项目,您必须使用哪种validation…

这就是为什么你最好使用非静态方法并将其放在界面中,如:

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

实际上你应该做的是: – 从GUI / Web界面/任何东西中检索参数(String?) – 以良好格式解析String参数(String to int …) – 使用validation类validation这些参数(静态方法与否) – 如果没有validation – >向用户打印消息 – 其他构造对象 – 使用该对象

我在界面中看不到任何需要静态方法的东西……