为什么接口中没有静态方法,但是静态字段和内部类可以? [Java8之前的版本]
这里有一些问题,为什么不能在接口内定义静态方法,但是它们都不能解决基本的不一致性:为什么可以在接口内定义静态字段和静态内部类型,却不能定义静态方法?
静态内部类型可能不是一个公平的比较,因为那只是生成新类的语法糖,但是为什么要使用字段而不是方法呢?
反对接口内的静态方法的一个论点是,它破坏了JVM使用的虚拟表解析策略,但是这不应该同样适用于静态字段吗,即编译器可以内联它吗?
一致性是我所需要的,并且Java应该在接口内不支持任何形式的静态变量,或者应该保持一致并允许它们。
解决方案
我想到两个主要原因:
- Java中的静态方法不能被子类覆盖,这比静态字段对方法的影响要大得多。在实践中,我什至从不想覆盖子类中的字段,但我一直都覆盖方法。因此拥有静态方法会阻止实现该接口的类提供其自己的方法实现,这在很大程度上违反了使用接口的目的。
- 接口不应该包含代码;那就是抽象类的目的。接口的全部要点是让我们谈论可能不相关的对象,它们碰巧都具有一组特定的方法。实际上,提供这些方法的实现超出了预期的接口范围。
只能在接口中声明静态的final字段(与方法非常相似,即使我们不包括" public"关键字,它们也是公共的,而无论是否包含关键字,静态字段都是" final")。
这些只是值,无论在编译时使用它们的什么地方,它们都会被字面复制,因此我们永远不会在运行时真正"调用"静态字段。拥有静态方法不会具有相同的语义,因为它将涉及在没有实现的情况下调用接口,而Java是不允许的。
在接口中声明静态方法永远是没有意义的。它们不能通过常规调用MyInterface.staticMethod()执行。 (编辑:由于最后一句话使某些人感到困惑,因此,调用MyClass.staticMethod()可以在MyClass上精确执行staticMethod的实现,如果MyClass是接口,则该方法将不存在!)那么我们必须知道实际的类,因此接口是否包含它是无关紧要的。
更重要的是,静态方法永远不会被覆盖,如果我们尝试这样做:
MyInterface var = new MyImplementingClass(); var.staticMethod();
静态规则说必须执行声明的var类型中定义的方法。由于这是一个接口,因此这是不可能的。
当然,我们总是可以从方法中删除static关键字。一切都会正常。如果从实例方法中调用它,则可能必须禁止显示某些警告。
为了回答下面的一些评论,不能执行" result = MyInterface.staticMethod()"的原因是它必须执行MyInterface中定义的方法的版本。但是在MyInterface中不能定义一个版本,因为它是一个接口。它没有定义的代码。
在Java 5之前,静态字段的常见用法是:
interface HtmlConstants { static String OPEN = "<"; static String SLASH_OPEN = "</"; static String CLOSE = ">"; static String SLASH_CLOSE = " />"; static String HTML = "html"; static String BODY = "body"; ... } public class HtmlBuilder implements HtmlConstants { // implements ?!? public String buildHtml() { StringBuffer sb = new StringBuffer(); sb.append(OPEN).append(HTML).append(CLOSE); sb.append(OPEN).append(BODY).append(CLOSE); ... sb.append(SLASH_OPEN).append(BODY).append(CLOSE); sb.append(SLASH_OPEN).append(HTML).append(CLOSE); return sb.toString(); } }
这意味着HtmlBuilder不必限定每个常量,因此可以使用OPEN而不是HtmlConstants.OPEN
以这种方式使用工具最终会造成混乱。
现在使用Java 5,我们可以使用import static语法来达到相同的效果:
private final class HtmlConstants { ... private HtmlConstants() { /* empty */ } } import static HtmlConstants.*; public class HtmlBuilder { // no longer uses implements ... }
接口的目的是在不提供实现的情况下定义合同。因此,我们不能拥有静态方法,因为它们必须在接口中已经具有实现,因为我们不能覆盖静态方法。对于字段,只允许使用静态的"最终字段",它们本质上是常量(在1.5+中,接口中也可以有枚举)。常量可以帮助定义没有魔术数字的接口。
顺便说一句,因为接口只允许使用静态final字段,所以无需为接口中的字段显式指定" static final"修饰符。
我将继续讨论我的宠物理论,即在这种情况下缺乏一致性是为了方便而不是设计或者必要性,因为我没有听到令人信服的论点,认为这是两者中的一个。
存在静态字段(a)因为它们在JDK 1.0中存在,并且许多狡猾的决定是在JDK 1.0中做出的,并且(b)接口中的static final字段是Java当时最接近常量的东西。
允许在接口中使用静态内部类,因为这是纯语法糖,内部类实际上与父类无关。
因此,不允许静态方法仅是因为没有足够的理由这样做。一致性不足以迫使人们改变现状。
当然,在将来的JLS版本中可以允许这样做而不会破坏任何内容。
实际上,有时候有人可以从静态方法中受益是有原因的。它们可以用作实现接口的类的工厂方法。例如,这就是我们现在在openjdk中具有Collection接口和Collections类的原因。因此,存在一些变通办法,因为总是为另一个类提供私有构造函数,该构造函数将用作静态方法的"命名空间"。
原因是,无论我们是否显式声明该修饰符,接口中定义的所有方法都是抽象的。抽象静态方法不允许使用修饰符,因为静态方法不能被覆盖。
至于为什么接口允许使用静态字段。我有一种应该被认为是"功能"的感觉。我唯一想到的可能是对接口实现感兴趣的常量进行分组。
我同意一致性会是一个更好的方法。接口中不得包含静态成员。
静态方法绑定到一个类。在Java中,从技术上讲,接口不是类,而是类型,而不是类(因此,关键字实现,并且接口不扩展Object)。因为接口不是类,所以它们不能具有静态方法,因为没有要添加的实际类。
我们可以调用InterfaceName.class来获取与该接口相对应的Class对象,但是Class类专门声明它表示Java应用程序中的类和接口。但是,接口本身不被视为类,因此我们不能添加静态方法。
官方提出了允许Java 7接口中使用静态方法的提议。该提议是在Project Coin下提出的。
我个人认为这是个好主意。实施没有技术上的困难,这是非常合乎逻辑,合理的事情。我希望Project Coin中有一些建议,希望它们永远不会成为Java语言的一部分,但这是可以清除许多API的建议。例如,Collections类具有用于操作任何List实现的静态方法。这些可以包含在"列表"界面中。
更新:在Java Posse Podcast#234中,Joe D'arcy简要提到了该提案,称该提案"很复杂",可能不会在Project Coin的支持下进行。
更新:虽然他们没有将其纳入Java 7的Project Coin中,但Java 8确实支持接口中的静态功能。
我常常想知道为什么根本没有静态方法?它们确实有其用途,但是包/命名空间级别的方法可能会覆盖80种静态方法的用途。