编辑:从Java 8开始,静态方法现在被允许出现在接口中。

下面是例子:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

当然这行不通。但为什么不呢?

其中一个可能的问题是,当你调用:

IXMLizable.newInstanceFromXML(e);

在这种情况下,我认为它应该只调用一个空方法(即{})。所有子类都必须实现静态方法,所以在调用静态方法时它们都没问题。那为什么不可能呢?

编辑:我想我正在寻找比“因为这就是Java”更深刻的答案。

静态方法不能被覆盖是否有特殊的技术原因?也就是说,为什么Java的设计者决定让实例方法可重写,而不是静态方法?

编辑:我的设计的问题是我试图使用接口来执行编码约定。

也就是说,接口的目标有两个:

我希望IXMLizable接口允许我将实现它的类转换为XML元素(使用多态性,工作正常)。 如果有人想创建实现IXMLizable接口的类的新实例,他们总是知道会有一个newInstanceFromXML(Element e)静态构造函数。

除了在界面中添加注释之外,还有其他方法可以确保这一点吗?


当前回答

接口中静态方法的需求是什么,静态方法基本上是在你不需要创建对象实例时使用的,接口的整个思想是引入面向对象的概念,通过引入静态方法,你从概念中转移。

其他回答

“是否有一个特殊的原因,静态方法不能被覆盖”。

让我把这个问题重新定义一下。

“在编译时解析的方法不能在运行时解析,是否有特殊原因?”

或者,更完整地说,如果我想在没有实例的情况下调用一个方法,但知道这个类,我怎么能根据我没有的实例来解析它呢?

Java 8允许静态接口方法

在Java 8中,接口可以有静态方法。它们也可以有具体的实例方法,但没有实例字段。

这里有两个问题:

为什么在糟糕的过去,接口不能包含静态方法? 为什么静态方法不能被覆盖?

接口中的静态方法

在以前的版本中,接口不能有静态方法并没有强有力的技术原因。一个重复问题的海报很好地总结了这一点。静态接口方法最初被认为是一个小的语言变化,然后有一个官方提议在Java 7中添加它们,但后来由于不可预见的复杂性而被放弃。

最后,Java 8引入了静态接口方法,以及使用默认实现重写实例方法。但它们仍然不能有实例字段。这些特性是lambda表达式支持的一部分,您可以在JSR 335的H部分中阅读有关它们的更多信息。

覆盖静态方法

第二个问题的答案有点复杂。

静态方法在编译时可解析。动态分派对于实例方法很有意义,因为在实例方法中,编译器不能确定对象的具体类型,因此不能解析要调用的方法。但是调用静态方法需要一个类,由于该类在编译时是静态已知的,因此动态分派是不必要的。

了解实例方法如何工作的一些背景知识对于理解这里发生的事情是必要的。我相信实际的实现是完全不同的,但是让我解释一下方法分派的概念,它准确地模拟了观察到的行为。

Pretend that each class has a hash table that maps method signatures (name and parameter types) to an actual chunk of code to implement the method. When the virtual machine attempts to invoke a method on an instance, it queries the object for its class and looks up the requested signature in the class's table. If a method body is found, it is invoked. Otherwise, the parent class of the class is obtained, and the lookup is repeated there. This proceeds until the method is found, or there are no more parent classes—which results in a NoSuchMethodError.

如果一个超类和一个子类在它们的表中都有相同方法签名的条目,则首先遇到子类的版本,而从不使用超类的版本——这就是“重写”。

现在,假设我们跳过对象实例,只从一个子类开始。解析可以像上面那样进行,为您提供一种“可重写的”静态方法。但是,解析都可以在编译时发生,因为编译器是从已知的类开始的,而不是等到运行时才查询未指定类型的对象的类。“重写”静态方法没有意义,因为总是可以指定包含所需版本的类。


构造函数“接口”

这里有更多的材料来解决最近对这个问题的编辑。

听起来好像您希望有效地为IXMLizable的每个实现强制使用一个类构造函数方法。暂时不要试图通过接口强制实现这一点,假设您有一些满足此需求的类。你会如何使用它?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

由于在“构造”新对象时必须显式地将具体类型命名为Foo,因此编译器可以验证它确实具有必要的工厂方法。如果没有,那又怎样?如果我可以实现一个缺少“构造函数”的IXMLizable,并且我创建了一个实例并将其传递给您的代码,那么它就是一个具有所有必要接口的IXMLizable。

构造是实现的一部分,而不是接口。任何成功使用接口的代码都不关心构造函数。任何关心构造函数的代码都需要知道具体类型,而接口可以被忽略。

这个问题已经被问过了

重复我的回答:

在接口中声明静态方法从来没有意义。它们不能通过正常调用MyInterface.staticMethod()来执行。如果您通过指定实现类myimplemtor . staticmethod()来调用它们,那么您必须知道实际的类,因此接口是否包含它是无关紧要的。

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

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

static的规则说必须执行在声明的var类型中定义的方法。因为这是一个接口,所以这是不可能的。

不能执行“result=MyInterface. staticmethod()”的原因是它必须执行MyInterface中定义的方法的版本。但MyInterface中不能定义一个版本,因为它是一个接口。它没有定义上的代码。

虽然你可以说这相当于“因为Java是这样做的”,但实际上,这个决定是其他设计决策的逻辑结果,也有很好的理由。

一个接口永远不能被静态地解引用,例如issomething .member。接口总是通过引用该接口子类实例的变量来解除引用。因此,如果没有其子类的实例,接口引用永远不可能知道它引用的是哪个子类。

Thus the closest approximation to a static method in an interface would be a non-static method that ignores "this", i.e. does not access any non-static members of the instance. At the low-level abstraction, every non-static method (after lookup in any vtable) is really just a function with class scope that takes "this" as an implicit formal parameter. See Scala's singleton object and interoperability with Java as evidence of that concept. And thus every static method is a function with class scope that does not take a "this" parameter. Thus normally a static method can be called statically, but as previously stated, an interface has no implementation (is abstract).

Thus to get closest approximation to a static method in an interface, is to use a non-static method, then don't access any of the non-static instance members. There would be no possible performance benefit any other way, because there is no way to statically link (at compile-time) a ISomething.member(). The only benefit I see of a static method in an interface is that it would not input (i.e. ignore) an implicit "this" and thus disallow access to any of the non-static instance members. This would declare implicitly that the function that doesn't access "this", is immutate and not even readonly with respect to its containing class. But a declaration of "static" in an interface ISomething would also confuse people who tried to access it with ISomething.member() which would cause a compiler error. I suppose if the compiler error was sufficiently explanatory, it would be better than trying to educate people about using a non-static method to accomplish what they want (apparently mostly factory methods), as we are doing here (and has been repeated for 3 Q&A times on this site), so it is obviously an issue that is not intuitive for many people. I had to think about it for a while to get the correct understanding.

在接口中获取可变静态字段的方法是在接口中使用非静态getter和setter方法,来访问子类中的静态字段。旁注,显然不可变静态可以在带有静态final的Java接口中声明。

接口中静态方法的需求是什么,静态方法基本上是在你不需要创建对象实例时使用的,接口的整个思想是引入面向对象的概念,通过引入静态方法,你从概念中转移。