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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2689312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:22:25  来源:igfitidea点击:

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

javainterfacestaticabstract

提问by Tobias Kienzler

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?".

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



编辑:我原来的例子是病态的,但我会把它留在下面。

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 ParametricFunctionexample 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 ArrayIndexOutOfBoundsExceptionmust be considered as initialisation-check), so for time-critical applications I'd prefer the constructor-method:

我再举个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) { ... }
}

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)

这就涉及到另一个问题,接口中的构造函数是不可能的。那里有什么好的解决方案?我当然可以使用这种init(double[] parameters)方法,但我提到了我为什么不这样做的原因。
(编辑:好的,这里是一个实现接口的抽象类就可以了)

Now let's assume the ParametricFunctionallows 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 ParametricFunctioninterface does define this static method.

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

I know this example is rather artificial, and the reason for not simply using a initmethod through the interface is debatable, I'd still like to know the answer. Consider it an academic question if you don't like it.

我知道这个例子很人为,不简单地init通过接口使用方法的原因是有争议的,我仍然想知道答案。如果您不喜欢,请将其视为学术问题。

(original example)

(原始示例)

So basically I want one Interface to provide both usual methods and e.g. a getSimilarObjectmethod. For (a made up) example

所以基本上我想要一个接口来提供常用的方法和例如一种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);
}

and then

接着

public class Parabola implements ParametricFunction {
  /** @return f(x) = parameters[0] * x2 + 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 ParametricFunctions 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 ParametricFunctions, but every time a new one is implemented one has to remember adding it there, too.

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

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

采纳答案by Donal Fellows

You cannotrequire 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.

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

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!

最简单的方法绝对是使用某种工厂类来生成其他工厂类的实例。是的,这确实意味着您必须记住在添加新实例时使该工厂保持最新状态,但是由于您在进行新实现时所做的第一件事是对其进行测试(您确实对其进行了测试,是吗?)会很快解决这个问题!

回答by Grundlefleck

Why not try Java 5 enum? ie:

为什么不试试 Java 5 枚举呢?IE:

public enum ParametricFunctions implements ParametricFunction {
    Parabola() {
        /** @return f(x) = parameters[0] * x2 + 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] * x2 + 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:

然后,您可以拥有许多单独的实现文件,并将它们组合成一个更小的类枚举类,通过该类可以静态访问函数。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);
}

回答by Stephen C

Your answer to your own question can be simplified further. Keep the ParametricFunctioninterface as-is, and change Parabolainto 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.

确实,如果Parabola 需要成为单例类没有特别的原因,你可以去掉静态方法和属性,并将构造函数公开。

The purposeof creating an instance of Parabolais to simplify your application.

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

EDITin 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 thingas an abstract static method in Java.

您不能使用标准 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. 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.

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

Besides, I cannot think of a convincing reason why you needthe class to be a singleton; i.e. why the getInstancemethod is necessary.

此外,我想不出一个令人信服的理由来说明为什么您需要该类成为单身人士;即为什么该getInstance方法是必要的。

回答by Michael Borgwardt

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.

这背后的想法是将几个 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.

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

回答by Sebastien Lorber

The reason is readability: fit("Parabola", xValues, fValues) vs. fit(Parabola.getInstance(), xValues, fValues) vs. fit(new Parabola(), xValues, fValues). Why would I want to have an Instance of function defined entirely by it's arguments with no internal data?

原因是可读性:fit("Parabola", xValues, fValues) vs. fit(Parabola.getInstance(), xValues, fValues) vs. fit(new Parabola(), xValues, fValues)。为什么我想要一个完全由它的参数定义而没有内部数据的函数实例?

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

回答by Sebastien Lorber

@Sebastien: Why is there no interest for both classes to share the exact same static method name? 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. 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

@Sebastien:为什么两个类都没有兴趣共享完全相同的静态方法名称?使用反射这可能是确保该方法存在的唯一方法。我希望 getDescription() 返回类的描述。为什么它应该在不同的情况下改变?这就是为什么我希望这个方法是静态的,并且以类似接口的方式实施它。– 托拜厄斯·基恩茨勒 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. 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...

如果您希望 getDescription 返回相同的描述,而不管涉及的 ParametricFunction 实例,只需将 ParametricFunction 设为抽象类并直接在该类中实现静态方法。然后你就可以调用 A、I 或 B.getDescription(); (甚至 a,i 或 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. – Tobias Kienzler 5 hours ago

因为我希望 A 和 B 确定地实现那个 staticMethod 并确保其他人使用新类的接口也会这样做。– 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...

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

回答by Sebastien Lorber

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

接口中的构造函数?嗯?您希望能够调用 Interface i = new Interface(double[] parameters) 吗?而计算机又会选择自己执行吗?这和界面中的静态一样奇怪: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..."

正如你所说,应该在构造之前检查参数......但这并不意味着如果参数不正确,你就不能在构造时引发异常。这只是您可以添加的一种安全性,它将确保构造的对象是连贯的。但是这样的代码不允许您绕过先前的验证:在构造时引发异常会告诉您“嘿,您有一个错误!” 虽然不验证参数只会告诉你“hoho 使用 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); if ( pamFunc instanceOf ParabolaFunction ) return ParabolaFunction.validateParabola(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); }

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! 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...

???这是没有意义的,因为 JVM 根本无法知道要使用静态方法的哪个实现,因为您不是从实例而是从类调用静态方法!只有当您直接在 ParametricFunction 类中实现验证方法时才有意义,但无论如何,如果您做这样的事情,您将必须做与我之前使用 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 界面 / 任何东西中检索参数(字符串?) - 以良好的格式解析字符串参数(字符串到 int ...) - 使用验证类验证这些参数(静态方法与否) - 如果没有验证 -> 向用户打印消息 - 否则构造对象 - 使用该对象

I don't see anywhere a need of static method in interface...

我在接口中看不到任何地方需要静态方法......

回答by Ha.

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.

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

回答by Sebastien Lorber

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() ??? will it use the implementation of A or B?!!

试想一下,如果您调用 I.staticMethod() ,计算机将如何知道该怎么做???它会使用 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?

我想知道你想如何调用你的静态方法,你有示例代码来展示吗?