Why no static methods in Interfaces, but static fields and inner classes OK? [pre-Java8]
这里有几个问题问你为什么不能在接口中定义静态方法,但它们都没有解决一个基本的不一致性:为什么你能在接口中定义静态字段和静态内部类型,而不是静态方法?
静态内部类型可能不是一个公平的比较,因为这只是生成一个新类的语法糖,但是为什么字段而不是方法?
反对接口内静态方法的一个论点是,它破坏了JVM使用的虚拟表解析策略,但这不应该同样适用于静态字段,即编译器可以直接内联它吗?
一致性是我所希望的,Java应该在接口中不支持任何形式的静态,或者它应该是一致的并允许它们。
已经提出了一个官方建议,允许在Java 7中的接口中使用静态方法。这项提议是根据Coin项目提出的。
我个人认为这是个好主意。在实现过程中没有技术困难,这是一件非常合乎逻辑、合理的事情。有几个项目硬币的建议,我希望永远不会成为Java语言的一部分,但这是一个可以清理很多API。例如,
更新:在JAVA POSSE播客234中,乔D'ARCY简要地提到了这个提议,说它是"复杂的",并且可能不会在项目硬币下进行。
更新:虽然他们没有把它投向Java 7的Project Engin,但是Java 8确实支持接口中的静态功能。
我要用我最喜欢的理论来解释这一点,也就是说,在这种情况下,缺乏一致性是一个方便的问题,而不是设计或必要性的问题,因为我没有听到任何令人信服的论据表明这两者都是。
静态字段存在于(a)中,因为它们在JDK 1中存在,并且在JDK 1中做出许多轻率的决定,以及(b)接口中的静态最终字段是Java在时间上最接近的常数。
接口中的静态内部类是允许的,因为这纯粹是语法上的甜头——内部类实际上与父类没有任何关系。
因此,不允许使用静态方法,因为没有令人信服的理由这样做;一致性不足以改变现状。
当然,这在将来的JLS版本中是允许的,而且不会破坏任何东西。
在接口中声明静态方法从来没有意义。它们不能由普通调用MyInterface.StaticMethod()执行。(编辑:由于最后一句话让一些人困惑,调用myClass.staticMethod()会精确地在myClass上执行staticMethod的实现,如果myClass是接口,就不可能存在!)如果您通过指定实现类myImplementor.staticMethod()来调用它们,那么您必须知道实际的类,因此无论接口是否包含它都是不相关的。
更重要的是,静态方法永远不会被重写,如果您尝试这样做:
1 2 | MyInterface var = new MyImplementingClass(); var.staticMethod(); |
静态规则表示必须执行在声明的var类型中定义的方法。因为这是一个接口,所以这是不可能的。
当然,您可以从方法中移除静态关键字。一切都会好起来的。如果从实例方法调用某些警告,则可能需要取消这些警告。
要回答下面的一些注释,您不能执行"result=myInterface.staticMethod()"的原因是它必须执行在myInterface中定义的方法版本。但MyInterface中不能定义版本,因为它是一个接口。它没有定义的代码。
这是一条旧线,但这对所有人来说都是一个非常重要的问题。因为我今天注意到这一点,所以我试图用更清晰的方式来解释:
接口的主要目的是提供一些不可实现的东西,因此如果它们提供
static methods to be allowed
然后可以使用interfacename.staticMethodName()调用该方法,但这是未实现的方法,不包含任何内容。因此,允许静态方法是无用的。因此,他们根本不提供这一点。
static fields are allowed
因为字段不可实现,所以我的意思是您不能在字段中执行任何逻辑操作,您可以在字段上执行操作。所以你不能改变场的行为,这就是它们被允许的原因。
Inner classes are allowed
内部类是允许的,因为编译后创建了内部类的不同类文件,比如interfacename$innerclassname.class,所以基本上您是在不同的实体中一起提供实现,而不是在接口中。因此提供了内部类中的实现。
我希望这会有所帮助。
接口的目的是在不提供实现的情况下定义合同。因此,您不能拥有静态方法,因为它们必须在接口中已有一个实现,因为您不能重写静态方法。对于字段,只允许静态
顺便说一句,没有必要为接口中的字段显式地指定
在接口中没有静态方法没有真正的理由:Java语言设计者不希望这样。从技术角度来看,允许他们这样做是有道理的。毕竟抽象类也可以拥有它们。我假设但没有测试它,你可以在接口有静态方法的地方"手工制作"字节代码,并且它应该可以正常地调用方法和/或使用接口,而不会有任何问题。
实际上,有时有一些原因可以让人从静态方法中获益。它们可以用作实现接口的类的工厂方法。例如,这就是我们现在在OpenJDK中拥有Collection接口和Collections类的原因。因此,仍然有解决方法-为另一个类提供一个私有的构造函数,该构造函数将作为静态方法的"命名空间"。
在Java 5之前,静态字段的常用用法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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,我们有导入静态语法来实现相同的效果:
1 2 3 4 5 6 7 8 9 | private final class HtmlConstants { ... private HtmlConstants() { /* empty */ } } import static HtmlConstants.*; public class HtmlBuilder { // no longer uses implements ... } |
我经常想知道为什么静态方法?它们确实有自己的用途,但包/命名空间级别的方法可能涵盖了静态方法的80种用途。
静态方法绑定到类。在Java中,接口在技术上不是一个类,它是一个类型,而不是一个类(因此,关键字实现,而接口不扩展对象)。因为接口不是类,所以不能有静态方法,因为没有要附加到的实际类。
您可以调用IlFruteNe.class来获得与接口对应的类对象,但是类类具体地表示它代表Java应用程序中的类和接口。但是,接口本身不被视为类,因此不能附加静态方法。
引起人们注意的两个主要原因:
Java中的静态方法不能被子类重写,这是比静态字段更大的方法。实际上,我甚至不想重写子类中的字段,但我一直在重写方法。因此,拥有静态方法可以防止实现接口的类提供自己的方法实现,这在很大程度上破坏了使用接口的目的。
接口不应该有代码;这就是抽象类的用途。接口的全部要点是让您讨论可能不相关的对象,这些对象恰好都有一组特定的方法。实际上,提供这些方法的实现超出了接口的预期范围。
Java 1.8接口静态方法只对接口方法可见,如果我们从接口实例类中移除MultStudio()方法,我们无法将其用于InterfaceExample对象。但是,与其他静态方法一样,我们可以使用使用类名的接口静态方法。例如,有效的语句将是:exp1.methodSta1();
因此,在下面的例子中,我们可以说:1)Java接口静态方法是接口的一部分,我们不能用它来实现类对象。
2)Java接口静态方法有助于提供实用方法,例如空校验、集合排序、日志等。
3)Java接口静态方法帮助我们通过不允许实现类(IdFraseExcel)来覆盖它们来提供安全性。
4)我们不能为对象类方法定义接口静态方法,我们会得到编译错误,因为"这个静态方法不能从对象中隐藏实例方法"。这是因为它不允许在Java中,因为对象是所有类的基类,我们不能有一个类级静态方法和另一个具有相同签名的实例方法。
5)我们可以使用Java接口静态方法来移除诸如集合之类的实用类,并将其所有静态方法移到相应的接口,这很容易找到和使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 } |
我相信静态方法可以在不创建对象的情况下访问,并且接口不允许创建对象,因为这样会限制程序员直接使用接口方法,而不是从其实现的类。但是,如果您在一个接口中定义了一个静态方法,您可以直接访问它,而不需要它的实现。因此,接口中不允许使用静态方法。我不认为一致性是一个问题。
原因是,无论您是否显式声明修饰符,接口中定义的所有方法都是抽象的。抽象静态方法不是允许的修饰符组合,因为静态方法不能被重写。
至于为什么接口允许静态字段。我有一种感觉应该被认为是一个"特色"。我能想到的唯一可能性是对接口实现感兴趣的常量进行分组。
我同意一致性是更好的方法。接口中不允许有静态成员。
在一个接口中只能声明静态的final字段(很像方法,这些方法是公共的,即使您不包含"public"关键字,静态字段也是带有或不带关键字的"final"。
这些只是值,在编译时无论在何处使用它们,都将被逐字复制,因此您在运行时实际上从不"调用"静态字段。有一个静态方法不会有相同的语义,因为它涉及调用一个没有Java实现的接口,而Java是不允许的。