为什么我不能在接口中声明静态方法?

这个主题说的最多——静态方法不能在接口中声明的原因是什么?

public interface ITest {
public static String test();
}

上面的代码给出了以下错误(至少在 Eclipse 中) : “接口方法 ITest.test ()的非法修饰符; 只允许公共和抽象”。

106607 次浏览

静态方法不是实例方法,没有实例上下文,因此从接口实现它没有什么意义。

我举个例子来回答你的问题。假设我们有一个带有静态方法 add 的 Math 类。你可以这样调用这个方法:

Math.add(2, 3);

如果 Math 是一个接口而不是一个类,它就不能有任何已定义的函数。因此,说一些像 Math.add (2,3)这样的东西是没有意义的。

接口中不能有静态方法的原因在于 Java 解析静态引用的方式。Java 在尝试执行静态方法时不会费心去查找类的实例。这是因为静态方法不依赖于实例,因此可以直接从类文件执行。假设接口中的所有方法都是抽象的,那么 VM 将不得不寻找接口的特定实现,以便找到静态方法背后的代码,从而可以执行它。这与静态方法解析的工作方式相矛盾,并且会在语言中引入不一致性。

接口用于多态性,应用于 Object,而不是类型。因此(如前所述)使用静态接口成员毫无意义。

也许一个代码示例会有所帮助,我将使用 C # ,但是您应该能够遵循。

假设我们有一个名为 IPayable 的接口

public interface IPayable
{
public Pay(double amount);
}

现在,我们有两个具体的类来实现这个接口:

public class BusinessAccount : IPayable
{
public void Pay(double amount)
{
//Logic
}
}


public class CustomerAccount : IPayable
{
public void Pay(double amount)
{
//Logic
}
}

现在,让我们假设我们有一个不同帐户的集合,为此我们将使用 IPayable 类型的通用列表

List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());

现在,我们要向所有这些账户支付50美元:

foreach (IPayable account in accountsToPay)
{
account.Pay(50.00);
}

现在你知道接口是多么的有用了。

它们仅用于实例化对象,而不用于静态类。

如果你使得 pay 是静态的,那么当你在 Account ToPay 中循环访问 IPayable 时,就没有办法确定是否应该在 BusinessAccount 或 CustomerAccount 上调用 pay。

这里有一些问题。第一个问题是声明一个静态方法而不定义它。这就是

public interface Foo {
public static int bar();
}

还有

public interface Foo {
public static int bar() {
...
}
}

第一个是不可能的,原因是 埃斯波提到: 您不知道哪个实现类是正确的定义。

Java可以允许后者; 实际上,从 Java8开始,它就支持后者!

原因在于设计原则,java 不允许多重继承。下面的例子可以说明多重继承的问题:

public class A {
public method x() {...}
}
public class B {
public method x() {...}
}
public class C extends A, B { ... }

现在如果调用 C.x ()会发生什么?将执行 A.x ()还是 B.x () ?每一种有多重继承的语言都必须解决这个问题。

在 Java 中,接口允许某种受限制的多重继承。为了避免上面的问题,他们不允许有方法。如果我们看看接口和静态方法的相同问题:

public interface A {
public static method x() {...}
}
public interface B {
public static method x() {...}
}
public class C implements A, B { ... }

这里也有同样的问题,如果调用 C.x ()会发生什么?

有一个非常漂亮和简洁的答案,你的问题 给你。(它给我留下了深刻的印象,因为它是一种非常直接的解释方式,所以我想从这里将它链接起来。)

看起来接口中的静态方法可能在 爪哇8中得到支持,我的解决方案只是在内部类中定义它们。

interface Foo {
// ...
class fn {
public static void func1(...) {
// ...
}
}
}

同样的技术也可以用在注释中:

public @interface Foo {
String value();


class fn {
public static String getValue(Object obj) {
Foo foo = obj.getClass().getAnnotation(Foo.class);
return foo == null ? null : foo.value();
}
}
}

内部类应该始终以 Interface.fn...而不是 Class.fn...的形式访问,这样就可以消除模糊问题。

修饰语的非法组合: 静态和抽象

如果类的一个成员被声明为静态的,那么可以使用它的类名,该类名仅限于该类,而不需要创建对象。

如果类的一个成员被声明为抽象的,那么您需要将类声明为抽象的,并且您需要在它的继承类(Sub-Class)中提供抽象成员的实现。

您需要在子类中为类的抽象成员提供一个实现,在这个子类中,您将改变静态方法的行为,静态方法也被声明为抽象方法,它仅限于基类,这是不正确的

因为不能继承静态方法。所以没必要把它放在界面上。接口基本上是一个所有订阅者都必须遵守的契约。在接口中放置静态方法将强制订阅方实现该方法。它现在变得与静态方法不能被继承的事实相矛盾。

现在 Java8甚至允许我们在接口中定义静态方法。

interface X {
static void foo() {
System.out.println("foo");
}
}


class Y implements X {
//...
}


public class Z {
public static void main(String[] args) {
X.foo();
// Y.foo(); // won't compile because foo() is a Static Method of X and not Y
}
}

注意: 如果我们不显式地使用 Default/Static 关键字使它们成为 Default 方法和 Static 方法 rep,那么 Interface 中的方法在默认情况下仍然是公共抽象。

Java8改变了可以在接口中使用静态方法的世界,但是它强迫您为此提供实现。

public interface StaticMethodInterface {
public static int testStaticMethod() {
return 0;
}


/**
* Illegal combination of modifiers for the interface method
* testStaticMethod; only one of abstract, default, or static permitted
*
* @param i
* @return
*/
// public static abstract int testStaticMethod(float i);


default int testNonStaticMethod() {
return 1;
}


/**
* Without implementation.
*
* @param i
* @return
*/
int testNonStaticMethod(float i);

}

使用 爪哇8,接口现在可以具有静态方法。

例如,Compator 有一个静态 natalOrder ()方法。

接口不能有实现的要求也被放宽了。接口现在可以声明“ default”方法实现,这类似于一个例外的普通实现: 如果你从接口继承了一个默认实现,从超类继承了一个普通实现,超类的实现将始终具有优先级。