有没有办法确保实现接口的类实现静态方法?
首先,我阅读了埃里克森对 “为什么我不能在 Java 接口中定义静态方法?”。这个问题不是关于“为什么”,而是关于“然后怎么样?”。
Edit: my original example was ill-posed, but I'll leave it below.
虽然我现在确信在大多数情况下我想要做的事情是多余的,但在一种情况下可能需要它:
我将再次使用 ParametricFunction
示例。现在让我们来看一个复杂的函数,例如 贝塞尔函数,其中查找表是合适的。必须对其进行初始化,因此两个选项是将参数直接传递给构造函数或提供 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)
似乎更方便。但需要在构造之前检查参数,因此它必须是静态方法。这就是我真正想知道一种方法来确保每个实现 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<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});
。
First of all, I read erickson's useful reply to "Why can’t I define a static method in a Java interface?". This question is not about the "why" but about the "how then?".
Edit: my original example was ill-posed, but I'll leave it below.
While I am now convinced that in most cases what I want to do is overkill, there is one scenario where it could be needed:
I'll take the ParametricFunction
example again. Now let's take a complicated function, like the Bessel functions, where a lookup-table is appropriate. That has to be initialised, so the two options are passing the parameters directly to the constructor or providing a init(double[] parameters)
. The latter has the disadvantage that getValue(double x)
must check the initialisation every call (or the ArrayIndexOutOfBoundsException
must be considered as initialisation-check), so for time-critical applications I'd prefer the constructor-method:
interface ParametricFunction {
public double getValue(double x);
}
class BesselFunction implements ParametricFunction {
public BesselFunction(double[] parameters) { ... }
public double getValue(double x) { ... }
}
Which touches another problem, the impossibility of constructors in interfaces. What would be a good solution there? I could of course use the init(double[] parameters)
approach, but I mentioned my reason why not.
(Edit: OK, here an abstract class implementing the interface would do)
Now let's assume the ParametricFunction
allows only certain parameters, e.g. positive integers. How to check the vailidity of parameters passed to the constructor? Throwing an IllegalArgument
-exception would be a possibility, but a checkParametersValidity(double[] parameters)
seems a lot more convenient. But checking the parameters needs to be done before construction, so it has to be a static method. And that's where I'd really like to know a way to make sure every class implementing the ParametricFunction
interface does define this static method.
I know this example is rather artificial, and the reason for not simply using a init
method through the interface is debatable, I'd still like to know the answer. Consider it an academic question if you don't like it.
(original example)
So basically I want one Interface to provide both usual methods and e.g. a getSimilarObject
method. For (a made up) example
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);
}
and then
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);
}
}
Since this is not allowed in the current Java standard, what is the closest thing to this?
The idea behind this is putting several ParametricFunction
s in a package and use Reflection to list them all, allowing the user to pick e.g. which one to plot. Obviously one could provide a loader class containing an array of the available ParametricFunction
s, but every time a new one is implemented one has to remember adding it there, too.
edit: An example to call it is
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));
}
and calling evaluate("Parabola", 1, new double[]{1,2,0});
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您不能要求类通过接口实现特定的静态方法。这在 Java 术语中毫无意义。接口强制在实现该接口的类中存在特定的非静态方法;这就是他们所做的。
最简单的方法肯定是使用某种工厂类来生成其他类的实例。是的,这确实意味着您必须记住在添加新实例时使该工厂保持最新状态,但是由于您在进行新实现时所做的第一件事就是测试它(您确实测试了它,是吗?)我们很快就会发现这个问题!
You cannot require classes to implement particular static methods through an interface. It just makes no sense in Java terms. Interfaces force the presence of particular non-static methods in the classes that implement the interface; that's what they do.
The easiest way is definitely to have some sort of factory class that produces instances of the others. Yes, this does mean that you have to remember to keep that factory up to date when you add new instances, but since the first thing you do when you make a new implementation is test it (you do test it, yes?) you'll pick up on that problem very quickly!
为什么不尝试 Java 5 枚举呢?即:
使用此功能,您可以轻松查找静态函数类型,并且具有编译时安全性,但仍然允许在其他地方引用接口类型。您还可以在枚举类型上放置一个方法,以允许按名称查找函数。
编辑有关使用枚举方式的文件大小的问题。
在这种情况下,您可以将每个函数定义为它自己的类,即:
}
然后您可以拥有许多单独的实现文件,并将它们组合成一个更小的、类似枚举的类,通过该类可以静态访问函数。即:
这允许在一个地方查找函数,并且实现保持独立。您还可以将它们添加到静态集合中以进行名称查找。然后,您可以保持函数的可读性,如另一条评论中提到的:
Why not try Java 5 enum? ie:
With this you can look up the static function types easily, and with compile-time safety, but still allow the interface type to be referenced elsewhere. You could also place a method on the enum type to allow lookup of the function by name.
EDIT for concerns on file size with the enum way.
In that case you could define each function as it's own class, ie:
}
Then you can have many separate, implementation files, and compose them into one, smaller, enum-like class through which the functions can be accessed statically. Ie:
This allows a single place to look up the functions, with the implementation kept separate. You could also add them to a static collection for name lookup. You could then maintain readability in your functions as mentioned in another comment:
由于更基本的原因,这将失败:反射无法列出包中的所有类(因为由于类加载器机制的灵活性,“包中的所有类”不是一个明确定义的集合)。
此类事情的现代解决方案是通过依赖项注入框架使其成为应用程序配置的一部分。
嗯,按照你的概念,每次实现一个新的,就被迫将其放入同一个包中。通过将其放入配置文件或加载器类(实际上是同一件事),您可以消除该限制。
That's going to fail for a more basic reason: reflection offers no way to list all classes in a package (because "all classes in a package" is not a well-defined set, due to the flexibility of the classloader mechanism).
The modern solution for this kind of thing is to make it part of the application configuration via a dependency injection framework.
Well, with your concept, every time a new one is implemented, one is forced to put it into the same package. By putting it into a configuration file or a loader class (the same thing, really), you remove that restriction.
您对自己问题的回答可以进一步简化。保持 ParametricFunction 接口不变,并将 Parabola 更改为实现 ParametricFunction 的单例:
确实,如果没有特殊原因 Parabola 需要要成为单例类,您可以摆脱静态方法和属性并将构造函数公开。
创建
Parabola
实例的目的是简化您的应用程序。编辑回答您的以下问题:
您不能使用标准 Java 构造来强制类实现具有给定签名的静态方法。 Java 中不存在抽象静态方法。
您可以通过编写一个单独的工具来检查静态方法是否已实现,该工具作为构建的一部分运行并检查源代码或编译后的代码。但在我看来,这不值得付出努力。如果您编译调用它的代码,或者如果您尝试反射性地使用它,则在运行时,任何缺少的
getInstance()
都会显示出来。在我看来,这应该足够好了。此外,我想不出一个令人信服的理由为什么你需要该类成为单例;即为什么
getInstance
方法是必要的。Your answer to your own question can be simplified further. Keep the
ParametricFunction
interface as-is, and changeParabola
into a singleton that implementsParametricFunction
:Indeed, if there is no particular reason why Parabola needs to be a singleton class, you could get rid of the static method and attribute and make the constructor public.
The purpose of creating an instance of
Parabola
is to simplify your application.EDIT in response to your question below:
You cannot use standard Java constructs to force a class to implement a static method with a given signature. There is no such thing as an abstract static method in Java.
You could check that a static method is implemented by writing a separate tool that runs as part of your build and checks either the source code or the compiled code. But IMO, it is not worth the effort. Any missing
getInstance()
will show up if you compile code that calls it, or at runtime if you try to use it reflectively. That should be good enough, in my opinion.Besides, I cannot think of a convincing reason why you need the class to be a singleton; i.e. why the
getInstance
method is necessary.实际上,您缺少一些有关面向对象编程基础知识的东西...
如果您定义一个对象抛物线,该对象应该代表抛物线,而不是检查参数是否正常等的工具箱...
您的抛物线项目应该包含参数(x, y ...) 并且您可以使用构造函数传递它们...
因此您不应该在函数上使用参数,因为参数现在被声明为类成员属性...
然后只需调用
Actually you are missing something about oriented object programming basics...
If you define an object Parabola, this object should represent a Parabola, and not a toolbox to check parameters are ok etc...
Your Parabola item should contain the parameters (x, y ...) and you could pass them with constructor...
Thus you shouldn't use parameters on your function since the parameters are now declared as class member attributes...
Then just call
一种解决方案是使所有方法成为非静态方法,并要求类必须具有默认构造函数。然后您可以轻松实例化它并调用您需要的方法。
One solution - is make all methods non-static with requirement that class must have default constructor. Then you can instantiate it easily and call methods you need.
正如我已经说过的,将方法声明为静态意味着您可以直接从类中调用它,而不需要类实例。由于调用 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.meth() 或 b.meth() 因此,如果他创建一个 C 类并想要调用 C.meth() 他将永远无法做到因为 C.meth() 没有实现或者不是静态的...所以他会这样做,否则 C.meth() 永远不会被调用,那么强迫开发人员实现永远不会的静态函数也是没有意义的使用...
我不知道我可以添加什么...
As i already said declaring the method static means you can call it directly from the class and don't need a class instance. Since it has no sense to call I.staticMethod() (as already explained), you just can call A.staticMethod1() and B.staticMethod2(), their name doesn't matter at all since you call them from the A or B class, known at compile time!
If you want a getDescription to return the same description no matter the instance of ParametricFunction concerned, just make the ParametricFunction an abstract class and implement the static methods directly in this class. Then you'll be able to call A, I or B.getDescription(); (even a,i or b...). But it remains the same than implementing it in A and B and calling it threw A or B...
Calling a static method from an instance is not a good practice and has no interest, so you should call A.meth(), or B.meth() and not a.meth() or b.meth()
Actually "someone else" will normaly not call a.meth() or b.meth() thus if he makes a class C and want to call the C.meth() he'll never be able to do that because C.meth() is not implemented or not static... so he will do it, or the C.meth() would never been called and then it is also non sense to force developpers to implement static functions that would never be used...
I don't know what i can add...
接口中的构造函数?呃?您希望能够调用 Interface i = new Interface(double[]parameters) 吗?计算机下次会选择自己执行吗?这和接口中的静态一样奇怪:D
正如你所说,检查参数应该在构造之前完成......
但这并不意味着如果参数不正确,您就不能在构造时引发异常。这只是您可以添加的安全性,它将确保构造的对象是一致的。但是这样的代码不允许您绕过先前的验证:在构造时引发异常会告诉您“嘿,您有一个错误!”虽然不验证参数只会告诉您“hoho有人使用GUI试图设置一个错误的值,我们将向他发送一条错误消息......”
实际上,因为您需要验证这些值,并且该对象甚至没有构造,为什么你绝对想在模型对象上添加这个验证?表单/Gui/任何验证都可以在验证 java 类中的任何地方完成...
只需在另一个名为 ParametricFunctionValidationHelper 的类中设置一个静态(或非静态)方法,您可以在其中添加方法和验证实现。
如何表示您的 functionType 并不重要(我选择 String 因为我想您从用户界面、Web 或 gui 获取它...它可能是 Enum...
甚至可以在构造对象后验证该对象:
您 甚至可以将静态验证方法放入函数类中,然后您将拥有:
公共静态布尔 validateParametricFunction(ParametricFunction pamFunc) {
if ( pamFunc instanceOf BesselFunction ) 返回 BesselFunction.validateBessel(pamFunc.getParameters);
if ( pamFunc instanceOf ParabolaFunction ) 返回 ParabolaFunction.validateParabola(pamFunc.getParameters);
是
的,您将无法在接口中设置静态方法,但无论如何您将如何调用这样的方法?
使用类似
??? 的 代码这是没有意义的,因为 JVM 根本无法知道要使用静态方法的哪个实现,因为您不是从实例而是从类调用静态方法!仅当您直接在 ParametricFunction 类中实现 validate 方法时,它才有意义,但无论如何,如果您执行此类操作,您必须执行与我之前使用 instanceOf 向您展示的完全相同的操作,因为 pamFunc 的实例是您必须选择必须使用哪种验证的唯一项目...
这就是为什么您最好使用非静态方法并将其放入界面中,例如:
实际上您应该做的是:
- 从 GUI / Web 界面 / 任何东西检索参数(字符串?)
- 以良好的格式解析字符串参数(字符串到整数...)
- 使用验证类(静态方法或非静态方法)验证这些参数
- 如果没有验证->向用户打印消息
- 否则构造对象
对象......
- 使用我在接口中没有看到任何需要静态方法的
Constructor in interface? uh? Would you like to be able to call Interface i = new Interface(double[] parameters)? And the computer would another time choose himself the implementation? This is as much strange as static in interface :D
As you said, checking parameters should be done before construction...
But this doesn't mean you can't raise exception on construction if parameters are not ok. It's just a security you can add, that will ensure that the constructed object will be coherent. But such code doesn't allow you to bypass a previous validation: raising exception on construction will tell you "hey you have a bug!" while not validating the params will just tell you "hoho someone using GUI tried to set a bad value, we'll send him an error message..."
Actually, since you need to validate the values, and the object is not even constructed, why do you absolutly want to add this validation on the model object? Form/Gui/whatever validation could be done anywhere, in a validation java class...
Just set a static (or not) method, in another class called ParametricFunctionValidationHelper, where you add your method and the validation implementation.
It doesn't matter how is represented your functionType (i choose String because i suppose you get it from user interface, web or gui... it could have been Enum...
You can even validate the object after having constructed it:
You can even put static validation methods in function classes and then you'll have:
public static boolean validateParametricFunction(ParametricFunction pamFunc) {
if ( pamFunc instanceOf BesselFunction ) return BesselFunction.validateBessel(pamFunc.getParameters);
if ( pamFunc instanceOf ParabolaFunction ) return ParabolaFunction.validateParabola(pamFunc.getParameters);
}
Yes you won't be able to set the static method in the interface but anyway how would you call such a method?
With code like
??? This as no sense because the JVM won't be able at all to know which implementation of the static method to use since you don't call the static method from an instance but from a class! It as only sense if you implement the validate method directly in the ParametricFunction class, but anyway if you do such a thing you'll have to do exactly the same that i've shown you before with the instanceOf, because the instance of the pamFunc is the only item you'll have to select which kind of validation you'll have to use...
That's why you'd better use a non static method and put it in the interface like:
Actually what you should do is:
- Retrieve parameters (String?) from GUI / Web interface / anything
- Parse String parameters in good format (String to int...)
- Validate these parameters with a validation class (static method or not)
- If no validation -> print message to user
- Else construct object
- Use the object
I don't see anywhere a need of static method in interface...
你想做的事情是不行的...
你想在接口 I 中定义静态方法,并拥有该接口的一些实现 A 和 B,并在接口 I 中声明这些静态方法的自己的实现。
试想一下,如何如果你调用 I.staticMethod() ,计算机就会知道该怎么做???它会使用A还是B的实现吗?!!
在接口中声明方法的好处是使用多态性并能够为不同的对象实现调用此方法...但是对于静态方法,因为您不从实例调用该方法(实际上您可以但不是真的)需要...)但是对于ClassName.xxxMethod,它绝对没有兴趣...
因此您不必将这些静态方法放在接口中...只需将它们放在两个实现中并使用 A.staticMethod 调用它们() 和 B.staticMethod() (它们甚至不需要共享相同的方法名称!)
我想知道你想如何调用你的静态方法,你有示例代码来展示吗?
What you want to do is not ok...
You want to define static methods in an interface I and have some implementations A and B of this interface, with their own implementation of these static methods declared in the interface I.
Just imagine, how the computer would know what to do if you call I.staticMethod() ??? will it use the implementation of A or B?!!
The interest to declare a method in the interface is to use polymorphism and be able to call this method for different object implementations... But for static methods, since you don't call the method from an instance (actually you can but not really needed...) but with ClassName.xxxMethod, it absolutly has no interest...
Thus you don't have to put these static methods in the interface... just put them in the both implementations and call them with A.staticMethod() and B.staticMethod() (and they don't even need to share a same method name!)
I wonder how you want to call your static method, do you sample code to show?