为什么接口中没有静态方法,但是静态字段和内部类可以?[前Java8]

为什么接口中没有静态方法,但是静态字段和内部类可以?[前Java8],java,interface,jls,Java,Interface,Jls,这里有几个问题是关于为什么不能在接口中定义静态方法,但没有一个解决基本的不一致性:为什么可以在接口中定义静态字段和静态内部类型,而不能定义静态方法 静态内部类型可能不是一个公平的比较,因为这只是生成新类的语法糖,但为什么是字段而不是方法 反对接口内静态方法的一个论点是,它破坏了JVM使用的虚拟表解析策略,但这不应该同样适用于静态字段,即编译器可以直接内联它吗 一致性是我所希望的,Java应该不支持接口中任何形式的静态,或者应该是一致的并允许它们 想到两个主要原因: Java中的静态方法不能被子类

这里有几个问题是关于为什么不能在接口中定义静态方法,但没有一个解决基本的不一致性:为什么可以在接口中定义静态字段和静态内部类型,而不能定义静态方法

静态内部类型可能不是一个公平的比较,因为这只是生成新类的语法糖,但为什么是字段而不是方法

反对接口内静态方法的一个论点是,它破坏了JVM使用的虚拟表解析策略,但这不应该同样适用于静态字段,即编译器可以直接内联它吗


一致性是我所希望的,Java应该不支持接口中任何形式的静态,或者应该是一致的并允许它们

想到两个主要原因:

  • Java中的静态方法不能被子类覆盖,对于方法来说,这比静态字段要重要得多。实际上,我甚至不想重写子类中的字段,但我总是重写方法。因此,使用静态方法会阻止实现接口的类提供其自己的方法实现,这在很大程度上违背了使用接口的目的

  • 接口不应该有代码;这就是抽象类的用途。接口的全部要点是让您讨论可能不相关的对象,这些对象都有一组特定的方法。实际上,提供这些方法的实现超出了接口的预期范围


  • 在接口中只能声明静态final字段(很像方法,即使不包含“public”关键字,它们也是公共的,静态字段是带或不带关键字的“final”)


    这些只是值,在编译时,它们将被复制到任何使用它们的地方,所以您永远不会在运行时真正“调用”静态字段。拥有一个静态方法不会有相同的语义,因为它会涉及调用一个没有实现的接口,而Java不允许这样做。

    在接口中声明一个静态方法是没有意义的。它们不能由正常调用MyInterface.staticMethod()执行。(编辑:由于最后一句话让一些人感到困惑,调用MyClass.staticMethod()正好执行MyClass上的staticMethod实现,如果MyClass是接口,则不可能存在!)如果通过指定实现类MyImplementor.staticMethod()来调用它们,则必须知道实际的类,因此,接口是否包含它是无关紧要的

    更重要的是,静态方法永远不会被重写,如果您尝试执行以下操作:

    MyInterface var = new MyImplementingClass();
    var.staticMethod();
    
    static的规则规定必须执行在声明的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();
        }
    }
    

    接口的目的是定义合同而不提供实现。因此,您不能有静态方法,因为它们必须在接口中已经有一个实现,因为您不能重写静态方法。至于字段,只允许静态的
    最终字段
    ,这些字段本质上是常量(在1.5+中,接口中也可以有枚举)。这些常量有助于定义没有幻数的接口


    顺便说一句,没有必要为接口中的字段明确指定
    static final
    修饰符,因为只允许使用静态final字段。

    我将用我最喜欢的理论来解释这一点,即在这种情况下缺乏一致性是出于方便,而不是出于设计或必要,因为我没听过有说服力的论据说是这两个人中的任何一个

    静态字段的存在(a)是因为它们在JDK1.0中存在,而在JDK1.0中做出了许多狡猾的决定,(b)接口中的静态最终字段是java当时最接近常量的东西

    允许在接口中使用静态内部类,因为这是纯粹的语法糖——内部类实际上与父类无关

    所以静态方法是不允许的,因为没有令人信服的理由这样做;一致性不足以改变现状


    当然,在未来的JLS版本中,这是允许的,而不会破坏任何东西。

    实际上,有时有些人可以从静态方法中受益。它们可以用作实现接口的类的工厂方法。例如,这就是我们现在在openjdk中有Collection接口和Collections类的原因。因此,总是有一些变通方法-提供另一个具有私有构造函数的类,该构造函数将用作静态方法的“命名空间”。

    原因是,无论是否显式声明该修饰符,接口中定义的所有方法都是抽象的。抽象静态方法不是允许的修饰符组合,因为静态方法不能被重写

    至于为什么接口允许静态字段。我有一种感觉,这应该被视为一种“特征”。我能想到的唯一可能性是对接口实现感兴趣的常量进行分组

    我同意一致性是更好的方法。无统计
    private final class HtmlConstants {
        ...
        private HtmlConstants() { /* empty */ }
    }
    
    import static HtmlConstants.*;
    public class HtmlBuilder { // no longer uses implements
        ...
    }
    
    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
    }