简体   繁体   English

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

[英]Is there a way to make sure classes implementing an Interface implement static methods?

First of all, I read erickson's useful reply to "Why can't I define a static method in a Java interface?" 首先,我阅读了erickson对“为什么我不能在Java接口中定义静态方法?”的有用回复 . 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. 我将再次参加ParametricFunction示例。 Now let's take a complicated function, like the Bessel functions , where a lookup-table is appropriate. 现在让我们来看一个复杂的函数,比如Bessel函数 ,其中查找表是合适的。 That has to be initialised, so the two options are passing the parameters directly to the constructor or providing a init(double[] parameters) . 这必须初始化,因此两个选项是将参数直接传递给构造函数或提供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: 后者的缺点是getValue(double x)必须检查每次调用的初始化(或者ArrayIndexOutOfBoundsException必须被视为初始化检查),因此对于时间要求严格的应用程序,我更喜欢构造函数方法:

 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. 我当然可以使用init(double[] parameters)方法,但我之所以提到我的原因。
(Edit: OK, here an abstract class implementing the interface would do) (编辑:好的,这里实现接口的抽象类会这样做)

Now let's assume the ParametricFunction allows only certain parameters, eg positive integers. 现在让我们假设ParametricFunction只允许某些参数,例如正整数。 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. 抛出IllegalArgument checkParametersValidity(double[] parameters)是可能的,但checkParametersValidity(double[] parameters)似乎更方便。 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. 这就是我真正想知道的方法,以确保实现ParametricFunction接口的每个类都确定了这个静态方法。

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. 我知道这个例子是相当人为的,不仅仅是通过接口使用init方法的原因值得商榷,我仍然想知道答案。 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 eg a getSimilarObject method. 所以基本上我希望一个接口提供常用方法和例如getSimilarObject方法。 For (a made up) example 对于(一个组成的)例子

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);
  }
}

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? 由于在当前的Java标准中不允许这样做,最接近它的是什么?

The idea behind this is putting several ParametricFunction s in a package and use Reflection to list them all, allowing the user to pick eg which one to plot. 这背后的想法是将一些ParametricFunction放在一个包中,并使用Reflection将它们全部列出,允许用户选择哪一个绘图。 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. 显然,可以提供一个包含可用ParametricFunction数组的加载器类,但每次实现一个新的时,必须记住在那里添加它。

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}); 并调用evaluate("Parabola", 1, new double[]{1,2,0}); .

You cannot require classes to implement particular static methods through an interface. 不能要求类通过接口实现特定的静态方法。 It just makes no sense in Java terms. 在Java术语中它没有任何意义。 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! 是的,这确实意味着你必须记住在添加新实例时保持该工厂的最新状态,但是,当你做一个新的实现时,你要做的第一件事是测试它(你测试它,是吗?)你'我会很快发现这个问题!

Why not try Java 5 enum? 为什么不试试Java 5 enum? ie: 即:

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
}

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: 在这种情况下,您可以将每个函数定义为它自己的类,即:

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);
    }

} }

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: 即:

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

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: 然后,您可以在另一条评论中提到的函数中保持可读性:

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

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

The idea behind this is putting several ParametricFunction's in a package and use Reflection to list them all, allowing the user to pick eg which one to plot. 这背后的想法是将一些ParametricFunction放在一个包中,并使用Reflection将它们全部列出,允许用户选择哪一个绘图。

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. 这种事情的现代解决方案是通过依赖注入框架使其成为应用程序配置的一部分。

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. 显然,可以提供一个包含可用ParametricFunction数组的加载器类,但每次实现一个新的时,必须记住在那里添加它。

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. 通过将它放入配置文件或加载器类(实际上是同样的事情),您可以删除该限制。

Your answer to your own question can be simplified further. 您可以进一步简化对自己问题的回答。 Keep the ParametricFunction interface as-is, and change Parabola into a singleton that implements 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);
  }
}

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 . 创建Parabola实例的目的简化您的应用程序

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. 您不能使用标准Java构造来强制类实现具有给定签名的静态方法。 There is no such thing as an abstract static method in Java. 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. 但IMO,不值得努力。 Any missing getInstance() will show up if you compile code that calls it, or at runtime if you try to use it reflectively. 如果您编译调用它的代码,则会显示任何缺少的getInstance() ;如果您尝试反射性地使用它,则会在运行时显示。 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; 此外,我想不出一个令人信服的理由,为什么你需要这个班级成为一个单身人士; ie why the getInstance method is necessary. 即为什么getInstance方法是必要的。

The reason is readability: fit("Parabola", xValues, fValues) vs. fit(Parabola.getInstance(), xValues, fValues) vs. fit(new Parabola(), xValues, fValues). 原因是可读性:拟合(“抛物线”,x值,f值)与拟合(Parabola.getInstance(),xValues,fValues)对拟合(新抛物线(),x值,f值)。 Why would I want to have an Instance of function defined entirely by it's arguments with no internal data? 为什么我想要一个完全由它没有内部数据的参数定义的函数实例?

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... 您的抛物线项应包含参数(x,y ...),您可以使用构造函数传递它们...

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

Thus you shouldn't use parameters on your function since the parameters are now declared as class member attributes... 因此,您不应该在函数上使用参数,因为参数现在被声明为类成员属性...

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

Then just call 然后打电话

parabolaInstance.getValue();

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. 然后,您可以轻松地实例化它并调用您需要的方法。

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

Just imagine, how the computer would know what to do if you call I.staticMethod() ??? 试想一下,如果你打电话给I.staticMethod(),电脑怎么会知道怎么做? will it use the implementation of A or B?!! 它会使用A或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... 在接口中声明方法的兴趣是使用多态并且能够为不同的对象实现调用此方法...但对于静态方法,因为您不从实例调用该方法(实际上您可以但不是真的需要...)但是使用ClassName.xxxMethod,它绝对没有兴趣......

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

I wonder how you want to call your static method, do you sample code to show? 我想知道你想如何调用静态方法,你是否展示代码示例?

@Sebastien: Why is there no interest for both classes to share the exact same static method name? @Sebastien:为什么这两个类没有兴趣共享完全相同的静态方法名称? Using reflection this might be the only way to make sure the method exists. 使用反射这可能是确保方法存在的唯一方法。 I would like getDescription() to return the description of the class. 我想getDescription()返回类的描述。 Why should it change on different instances? 为什么它会在不同的情况下改变? That's why I'd like this method to be static and yet enforce in an Interface-like way that it is implemented. 这就是为什么我希望这种方法是静态的,并且以类似接口的方式强制执行它。 – Tobias Kienzler 3 - Tobias Kienzler 3

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! 因为没有理由调用I.staticMethod()(如已经解释过的),你可以调用A.staticMethod1()和B.staticMethod2(),因为你从A中调用它们,它们的名字根本不重要。 B类,编译时知道!

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. 如果您希望getDescription返回相同的描述,无论相关的ParametricFunction实例如何,只需将ParametricFunction设为抽象类并直接在此类中实现静态方法。 Then you'll be able to call A, I or B.getDescription(); 然后你就可以调用A,I或B.getDescription(); (even a,i or b...). (甚至是a,i或b ......)。 But it remains the same than implementing it in A and B and calling it threw A or B... 但它仍然与在A和B中实现它并且称它扔A或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() 从实例调用静态方法不是一个好习惯,没有兴趣,所以你应该调用A.meth()或B.meth()而不是a.meth()或b.meth()

Because I wanted A and B to implement that staticMethod for sure and make sure someone else using the Interface for a new class will do so, too. 因为我希望A和B确实实现staticMethod,并确保其他人使用Interface作为新类也会这样做。 – Tobias Kienzler 5 hours ago - Tobias Kienzler 5小时前

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

I don't know what i can add... 我不知道我能添加什么......

Constructor in interface? 接口中的构造函数? uh? 呃? Would you like to be able to call Interface i = new Interface(double[] parameters)? 您是否希望能够调用Interface i = new Interface(double []参数)? And the computer would another time choose himself the implementation? 而计算机又会选择自己实施吗? This is as much strange as static in interface :D 这和界面中的静态一样奇怪: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!" 但是这样的代码不允许你绕过先前的验证:在构造上引发异常将告诉你“嘿,你有一个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..." 虽然没有验证参数只会告诉你“有人使用GUI试图设置一个不好的价值,我们会给他发错误信息......”

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. Form / Gui /可以在任何地方进行验证,在验证java类中......只需在另一个名为ParametricFunctionValidationHelper的类中设置静态(或非)方法,在其中添加方法和验证实现。

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

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... 如何表示你的functionType并不重要(我选择String因为我想你是从用户界面,web或gui获得它...它可能是Enum ...

You can even validate the object after having constructed it: 您甚至可以在构造对象后验证它:

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

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); 您甚至可以在函数类中放置静态验证方法,然后您将拥有:public static boolean validateParametricFunction(ParametricFunction pamFunc){if(pamFunc instanceOf BesselFunction)return BesselFunction.validateBessel(pamFunc.getParameters); if ( pamFunc instanceOf ParabolaFunction ) return ParabolaFunction.validateParabola(pamFunc.getParameters); if(pamFunc instanceOf ParabolaFunction)返回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 用代码就好

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

??? ??? 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! 这没有任何意义,因为JVM根本无法知道要使用静态方法的哪个实现,因为您不从实例调用静态方法而是从类调用静态方法! 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... 只有在参数化函数类中直接实现validate方法才有意义,但无论如何,如果你做了这样的事情,你必须做的事与我之前用instanceOf完全相同,因为pamFunc的实例是您必须选择的唯一项目,您必须使用哪种验证...

That's why you'd better use a non static method and put it in the interface like: 这就是为什么你最好使用非静态方法并将其放在界面中,如:

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

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

I don't see anywhere a need of static method in interface... 我在界面中看不到任何需要静态方法的东西......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM