153

该主题说明了大部分内容-无法在接口中声明静态方法的原因是什么?

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

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

4

14 回答 14

86

这里有几个问题。第一个是声明静态方法而不定义它的问题。这是之间的区别

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

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

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

Java可以允许后者;事实上,从 Java 8 开始,它确实如此!

于 2008-08-22T14:29:02.467 回答
44

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

于 2008-08-22T05:20:28.977 回答
18

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

Math.add(2, 3);

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

于 2008-08-22T05:19:16.793 回答
11

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

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

现在如果你调用 Cx() 会发生什么?将执行 Ax() 或 Bx() 吗?每种具有多重继承的语言都必​​须解决这个问题。

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

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

同样的问题,如果你调用 Cx() 会发生什么?

于 2008-09-26T08:48:34.167 回答
7

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

于 2008-08-22T05:18:15.767 回答
6

现在 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 将它们分别设为默认方法和静态方法,则接口中的方法默认仍然是公共抽象的。

于 2014-03-28T11:19:28.817 回答
4

您的问题在这里有一个非常简洁明了的答案。(它以一种非常直接的方式来解释它,我想从这里链接它,这让我印象深刻。)

于 2009-02-06T12:19:36.713 回答
3

似乎Java 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...,然后,您可以摆脱模棱两可的问题。

于 2013-09-17T04:09:24.360 回答
2

接口用于多态性,它适用于对象,而不是类型。因此(如前所述)拥有静态接口成员是没有意义的。

于 2008-08-22T05:27:15.870 回答
1

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

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);

}

于 2016-02-05T09:49:02.427 回答
0

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

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

如果一个类的成员被声明为抽象,则需要将该类声明为抽象,并且需要在其继承的类(子类)中提供抽象成员的实现。

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

于 2013-10-02T10:32:48.717 回答
0

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

于 2013-12-15T05:00:13.927 回答
0

Java 8中,接口现在可以拥有静态方法。

例如,Comparator 有一个静态 naturalOrder() 方法。

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

于 2019-02-21T11:20:23.583 回答
-2

也许一个代码示例会有所帮助,我将使用 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.00 美元:

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

所以现在你看到了接口是如何非常有用的。

它们仅用于实例化对象。不在静态类上。

如果您已将 pay 设为静态,则当在 accountsToPay 中循环通过 IPayable 时,将无法确定它是否应该在 BusinessAcount 或 CustomerAccount 上调用 pay。

于 2008-08-22T05:35:52.073 回答