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

时间:2008-09-24 19:19:28

标签: java interface jls

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

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

接口中静态方法的一个参数是它破坏了JVM使用的虚拟表解析策略,但不应该同样适用于静态字段,即编译器只能内联它?

一致性是我想要的,Java应该支持接口中没有任何形式的静态,或者它应该是一致的并且允许它们。

15 个答案:

答案 0 :(得分:49)

{7}}允许在Java 7中的接口中使用静态方法。此提议是在official proposal下进行的。

我个人认为这是一个好主意。实施中没有技术上的困难,这是一个非常合乎逻辑的合理的事情。 Project Coin中有几个提议我希望永远不会成为Java语言的一部分,但这可以清理很多API。例如,Project Coin用于操纵任何List实现;这些可以包含在List界面中。


更新:Collections class has static methods Joe D'Arcy简短地提到了这个提案,说这是“复杂的”,可能不会在Project Coin下进行。


更新:虽然它们没有进入Java 7的Project Coin,但Java 8确实支持Java Posse Podcast #234,

答案 1 :(得分:39)

我将使用我的宠物理论,这是因为在这种情况下缺乏一致性是一个方便而不是设计或必要性的问题,因为我没有听到任何令人信服的论据,即它是那两个。

静态字段存在(a),因为它们存在于JDK 1.0中,并且在JDK 1.0中做出了许多狡猾的决定,并且(b)接口中的静态最终字段是当时java最常见的常量字符。 / p>

允许接口中的静态内部类,因为这是纯语法糖 - 内部类实际上与父类无关。

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

当然,这可以在未来的JLS版本中被允许而不会破坏任何内容。

答案 2 :(得分:14)

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

更重要的是,静态方法永远不会被覆盖,如果你尝试这样做:

MyInterface var = new MyImplementingClass();
var.staticMethod();

static的规则说必须执行在声明的var类型中定义的方法。由于这是一个界面,这是不可能的。

您当然可以始终从方法中删除static关键字。一切都会好起来的。如果从实例方法调用它,则可能必须禁止某些警告。

要回答下面的一些注释,您无法执行“result = MyInterface.staticMethod()”的原因是它必须执行MyInterface中定义的方法版本。但MyInterface中不能定义版本,因为它是一个接口。根据定义,它没有代码。

答案 3 :(得分:6)

接口的目的是在不提供实现的情况下定义合同。因此,您不能拥有静态方法,因为它们必须已在接口中具有实现,因为您无法覆盖静态方法。至于字段,只允许静态final fields,它们本质上是常量(在1.5+中你也可以在接口中有枚举)。常量用于帮助定义没有幻数的接口。

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

答案 4 :(得分:6)

这是一个老话题,但这对所有人来说都是非常重要的问题。因为我今天注意到了这一点,所以我试图以更清洁的方式解释它:

界面的主要目的是提供一些无法实现的东西,所以如果它们提供

  

允许使用静态方法

然后您可以使用 interfaceName.staticMethodName()调用该方法,但这是未实现的方法,不包含任何内容。因此,允许静态方法是没用的。因此他们根本不提供这一点。

  

允许使用静态字段

因为字段不可实现,可实现的意思是你不能在字段中执行任何逻辑操作,你可以对字段进行操作。因此,您不会改变字段的行为,这就是允许它们的原因。

  

允许内部类

允许使用内部类,因为在编译之后创建了Inner类的不同类文件,说 InterfaceName $ InnerClassName.class ,所以基本上你在不同的实体中提供实现,但不是在接口中。因此,提供了Inner类中的实现。

我希望这会有所帮助。

答案 5 :(得分:3)

在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
    ...
}

答案 6 :(得分:3)

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

答案 7 :(得分:3)

没有真正的理由在接口中没有静态方法,除了:Java语言设计者不希望它那样。 从技术角度来看,允许它们是有意义的。毕竟一个抽象类也可以拥有它们。我假设但是没有测试它,你可以“手工制作”接口有静态方法的字节代码,并且它应该可以毫无问题地调用方法和/或通常使用接口。

答案 8 :(得分:2)

我经常想知道为什么静态方法呢?它们确实有它们的用途,但是包/名称空间级方法可能会涵盖80种静态方法。

答案 9 :(得分:1)

有两个主要原因:

  1. Java中的静态方法不能被子类覆盖,这对于方法来说比静态字段要大得多。在实践中,我甚至都不想覆盖子类中的字段,但我一直都覆盖方法。因此,使用静态方法可以防止实现接口的类提供自己的方法实现,这在很大程度上违背了使用接口的目的。

  2. 接口不应该有代码;这就是抽象类的用途。接口的重点是让你谈谈可能不相关的对象,这些对象碰巧都有一组特定的方法。实际上,提供这些方法的实现超出了接口的范围。

答案 10 :(得分:1)

静态方法与类绑定。在Java中,接口在技术上不是类,它是类型,但不是类(因此,关键字实现,接口不扩展Object)。因为接口不是类,所以它们不能有静态方法,因为没有要附加的实际类。

您可以调用InterfaceName.class来获取与接口对应的Class Object,但Class类明确声明它表示Java应用程序中的类和接口。但是,接口本身不被视为类,因此您无法附加静态方法。

答案 11 :(得分:0)

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

这些只是值,并且将在编译时在任何地方进行字面复制,因此您永远不会在运行时“调用”静态字段。使用静态方法不会有相同的语义,因为它涉及调用没有实现的接口,Java不允许。

答案 12 :(得分:0)

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

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

我同意一致性是一种更好的方法。接口中不允许使用静态成员。

答案 13 :(得分:0)

我相信可以在不创建对象的情况下访问静态方法,并且接口不允许创建对象以限制程序员直接使用接口方法而不是从其实现的类。 但是,如果在接口中定义静态方法,则可以直接访问它而无需实现。因此,接口中不允许使用静态方法。 我不认为一致性应该是一个问题。

答案 14 :(得分:0)

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
}