提问人:Henrik Paul 提问时间:8/22/2008 最后编辑:skaffmanHenrik Paul 更新时间:2/21/2019 访问量:107520
为什么我不能在接口中声明静态方法?
Why can't I declare static methods in an interface?
问:
这个话题说得最多 - 静态方法不能在接口中声明的原因是什么?
public interface ITest {
public static String test();
}
上面的代码给了我以下错误(至少在 Eclipse 中):“接口方法 ITest.test() 的非法修饰符;只允许公开和抽象”。
答:
静态方法不是实例方法。没有实例上下文,因此从接口实现它意义不大。
我将用一个例子来回答你的问题。假设我们有一个带有静态方法添加的 Math 类。您可以像这样调用此方法:
Math.add(2, 3);
如果 Math 是一个接口而不是一个类,它就不能有任何定义的函数。因此,说像 Math.add(2, 3) 这样的东西是没有意义的。
接口中不能有静态方法的原因在于 Java 解析静态引用的方式。Java 在尝试执行静态方法时不会费心寻找类的实例。这是因为静态方法不依赖于实例,因此可以直接从类文件执行。鉴于接口中的所有方法都是抽象的,VM 必须查找接口的特定实现,以便找到静态方法背后的代码,以便可以执行它。这与静态方法解析的工作方式相矛盾,并且会在语言中引入不一致。
评论
接口用于多态性,它适用于对象,而不是类型。因此(如前所述)拥有静态接口成员是没有意义的。
评论
也许代码示例会有所帮助,我将使用 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);
}
所以现在你明白了接口是如何非常有用的。
它们仅用于实例化对象。不在静态类上。
如果您将支付设置为静态,则在 accountsToPay 中循环浏览 IPayable 时,将无法确定它是否应该在 BusinessAcount 或 CustomerAccount 上调用 pay。
评论
这里有一些问题在起作用。第一个问题是声明静态方法而不定义它的问题。这是两者的区别
public interface Foo {
public static int bar();
}
和
public interface Foo {
public static int bar() {
...
}
}
由于 Espo 提到的原因,第一个是不可能的:你不知道哪个实现类是正确的定义。
Java 可以允许后者;事实上,从 Java 8 开始,它就做到了!
评论
static
interface
public
原因在于设计原则,即 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() 会发生什么?
评论
A
int x(int z);
B
string x(int x);
x(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...
修饰符的非法组合:静态和抽象
如果一个类的成员被声明为静态的,则可以将其与限制在该类中的类名一起使用,而无需创建对象。
如果一个类的成员被声明为抽象,则需要将该类声明为抽象,并且需要在其继承的类(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 方法,则默认情况下,Interface 中的方法仍然是公共抽象的。
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);
}
在 Java 8 中,接口现在可以有静态方法。
例如,Comparator 有一个静态的 naturalOrder() 方法。
接口不能有实现的要求也放宽了。接口现在可以声明“默认”方法实现,这与普通实现类似,但有一个例外:如果同时从接口继承默认实现和从超类继承普通实现,则超类的实现将始终优先。
评论