这里有一些关于为什么不能在接口中定义静态方法的问题,但它们都没有解决基本的不一致性:为什么可以在接口中定义静态字段和静态内部类型,但不能定义静态方法?
静态内部类型可能不是一个公平的比较,因为这只是生成新类的语法糖,但为什么是字段而不是方法呢?
反对接口中的静态方法的一个论点是它破坏了 JVM 使用的虚拟表解析策略,但这不应该同样适用于静态字段,即编译器可以内联它吗?
一致性是我想要的,Java 应该在接口中不支持任何形式的静态,或者它应该是一致的并允许它们。
已经提出了在 Java 7 的接口中允许静态方法的官方提案。该提案是在Project Coin下提出的。
我个人的看法是,这是一个好主意。实现上没有技术难度,是一件非常合乎逻辑、合理的事情。我希望 Project Coin 中有几个提案永远不会成为 Java 语言的一部分,但这是一个可以清理大量 API 的提案。例如,该类具有用于操作任何实现的Collections
静态方法;List
这些可以包含在List
界面中。
更新:在Java Posse Podcast #234 中, Joe D'arcy 简要地提到了该提案,称它“复杂”并且可能不会在 Project Coin 中实现。
更新:虽然他们没有进入 Java 7 的 Project Coin,但 Java 8 确实支持接口中的静态函数。
我将使用我的宠物理论,即在这种情况下缺乏一致性是方便而不是设计或必要性的问题,因为我没有听到任何令人信服的论据表明它是这两者中的任何一个.
存在静态字段 (a) 因为它们在 JDK 1.0 中就存在,并且在 JDK 1.0 中做出了许多狡猾的决定,并且 (b) 接口中的静态最终字段是 Java 在当时最接近常量的东西。
允许使用接口中的静态内部类,因为那是纯粹的语法糖——内部类实际上与父类没有任何关系。
所以静态方法是不允许的,因为没有令人信服的理由这样做;一致性不足以改变现状。
当然,这可以在未来的 JLS 版本中被允许,而不会破坏任何东西。
在接口中声明静态方法是没有意义的。它们不能通过正常调用 MyInterface.staticMethod() 来执行。(编辑:由于最后一句话让一些人感到困惑,调用 MyClass.staticMethod() 精确地执行了 MyClass 上的 staticMethod 的实现,如果 MyClass 是一个接口则不存在!)如果你通过指定实现类来调用它们 MyImplementor.staticMethod()那么你必须知道实际的类,所以接口是否包含它是无关紧要的。
更重要的是,静态方法永远不会被覆盖,如果你尝试这样做:
MyInterface var = new MyImplementingClass();
var.staticMethod();
static 的规则说必须执行在声明的 var 类型中定义的方法。因为这是一个接口,所以这是不可能的。
当然,您始终可以从方法中删除 static 关键字。一切都会好起来的。如果从实例方法调用它,您可能必须禁止显示一些警告。
要回答下面的一些评论,您无法执行“result=MyInterface.staticMethod()”的原因是它必须执行 MyInterface 中定义的方法版本。但是在 MyInterface 中不能定义一个版本,因为它是一个接口。它没有定义的代码。
接口的目的是在不提供实现的情况下定义契约。因此,您不能拥有静态方法,因为它们必须在接口中已经有一个实现,因为您不能覆盖静态方法。至于字段,只final fields
允许静态,本质上是常量(在 1.5+ 中,您还可以在接口中使用枚举)。常量用于帮助定义没有幻数的接口。
顺便说一句,不需要为static final
接口中的字段显式指定修饰符,因为只允许静态最终字段。
这是一个旧线程,但这对所有人来说都是非常重要的问题。因为我今天才注意到这一点,所以我试图以更简洁的方式解释它:
接口的主要目的是提供一些无法实现的东西,所以如果他们提供
允许使用静态方法
然后您可以使用interfaceName.staticMethodName()调用该方法,但这是未实现的方法并且不包含任何内容。所以允许静态方法是没有用的。因此他们根本不提供这个。
允许使用静态字段
因为字段是不可实现的,所以可实现的意思是你不能在字段中执行任何逻辑操作,你可以在字段上进行操作。因此,您不会改变字段的行为,这就是允许它们的原因。
允许内部类
内部类是允许的,因为在编译后创建了内部类的不同类文件,比如InterfaceName$InnerClassName.class,所以基本上你是在不同的实体中提供实现,而不是在接口中。因此提供了内部类中的实现。
我希望这会有所帮助。
在 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
...
}
实际上,有时有人可以从静态方法中受益。它们可以用作实现接口的类的工厂方法。例如,这就是我们现在在 openjdk 中有 Collection 接口和 Collections 类的原因。所以总是有一些解决方法——为另一个类提供一个私有构造函数,它将作为静态方法的“命名空间”。
没有真正的理由在接口中没有静态方法,除了:Java 语言设计者不希望这样。从技术角度来看,允许它们是有意义的。毕竟抽象类也可以拥有它们。我假设但没有测试它,您可以在接口具有静态方法的情况下“手工制作”字节代码,并且它应该可以毫无问题地调用该方法和/或像往常一样使用接口。
我经常想知道为什么要使用静态方法?它们确实有其用途,但包/命名空间级别的方法可能会涵盖 80 种静态方法的用途。
两个主要原因浮现在脑海:
Java 中的静态方法不能被子类覆盖,这对方法来说比静态字段更重要。在实践中,我什至从不想覆盖子类中的字段,但我一直都在覆盖方法。因此,拥有静态方法会阻止实现接口的类提供它自己的该方法的实现,这在很大程度上违背了使用接口的目的。
接口不应该有代码;这就是抽象类的用途。接口的全部意义在于让您谈论可能不相关的对象,这些对象碰巧都有一组特定的方法。实际上,提供这些方法的实现超出了接口的预期范围。
静态方法绑定到一个类。在 Java 中,接口在技术上不是一个类,它是一个类型,但不是一个类(因此,关键字 implements,接口不扩展 Object)。因为接口不是类,所以它们不能有静态方法,因为没有要附加的实际类。
您可以调用InterfaceName.class 来获取接口对应的Class Object,但Class 类明确表示它代表Java 应用程序中的类和接口。但是,接口本身不被视为一个类,因此您不能附加静态方法。
只能在接口中声明静态最终字段(很像方法,即使您不包含“public”关键字也是公共的,静态字段在有或没有关键字的情况下都是“final”)。
这些只是值,在编译时无论在何处使用它们都会被逐字复制,因此您永远不会在运行时真正“调用”静态字段。拥有静态方法不会具有相同的语义,因为它会涉及调用没有实现的接口,这是 Java 不允许的。
原因是接口中定义的所有方法都是抽象的,无论您是否显式声明该修饰符。抽象静态方法不是修饰符的允许组合,因为静态方法不能被覆盖。
至于为什么接口允许静态字段。我有一种感觉,应该被视为“功能”。我能想到的唯一可能性是将接口的实现感兴趣的常量分组。
我同意一致性会是一种更好的方法。接口中不允许有静态成员。
我相信可以在不创建对象的情况下访问静态方法,并且接口不允许创建对象,以限制程序员直接使用接口方法而不是使用其实现的类。但是如果你在一个接口中定义了一个静态方法,你可以直接访问它而不需要它的实现。因此,接口中不允许使用静态方法。我不认为一致性应该是一个问题。
Java 1.8 接口静态方法仅对接口方法可见,如果我们从 InterfaceExample 类中删除 methodSta1() 方法,我们将无法将它用于 InterfaceExample 对象。然而,像其他静态方法一样,我们可以使用类名来使用接口静态方法。例如,一个有效的语句将是:exp1.methodSta1();
所以看了下面的例子我们可以说:1)Java接口静态方法是接口的一部分,我们不能将它用于实现类对象。
2) Java 接口静态方法有利于提供实用方法,例如空值检查、集合排序、日志等。
3)Java接口静态方法通过不允许实现类(InterfaceExample)覆盖它们来帮助我们提供安全性。
4)我们不能为Object类方法定义接口静态方法,我们会得到编译器错误“这个静态方法不能从Object隐藏实例方法”。这是因为它在 java 中是不允许的,因为 Object 是所有类的基类,我们不能拥有一个类级别的静态方法和另一个具有相同签名的实例方法。
5)我们可以使用java接口的静态方法去除Collections等实用类,并将其所有的静态方法移到对应的接口中,方便查找和使用。
public class InterfaceExample implements exp1 {
@Override
public void method() {
System.out.println("From method()");
}
public static void main(String[] args) {
new InterfaceExample().method2();
InterfaceExample.methodSta2(); // <--------------------------- would not compile
// methodSta1(); // <--------------------------- would not compile
exp1.methodSta1();
}
static void methodSta2() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= InterfaceExample :: from methodSta2() ======");
}
}
interface exp1 {
void method();
//protected void method1(); // <-- error
//private void method2(); // <-- error
//static void methodSta1(); // <-- error it require body in java 1.8
static void methodSta1() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= exp1:: from methodSta1() ======");
}
static void methodSta2() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= exp1:: from methodSta2() ======");
}
default void method2() { System.out.println("--- exp1:: from method2() ---");}
//synchronized default void method3() { System.out.println("---");} // <-- Illegal modifier for the interface method method3; only public, abstract, default, static
// and strictfp are permitted
//final default void method3() { System.out.println("---");} // <-- error
}