2010-04-22 134 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 ...) - 使用验证类验证这些参数(是否为静态方法) - 如果没有验证 - >打印消息给用户 - 其他构造对象 - 使用物品

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

相关问题