这是一个例子:
public interface IXMLizable{ 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部分中阅读更多相关内容.
覆盖静态方法第二个问题的答案有点复杂.
静态方法在编译时可以解析.动态分派对于实例方法是有意义的,其中编译器无法确定对象的具体类型,因此无法解析要调用的方法.但是调用静态方法需要一个类,因为该类是静态已知的- 编译时 - 动态调度是不必要的.
有关实例方法如何工作的一些背景知识是必要的,以了解这里发生了什么.我确信实际的实现是完全不同的,但让我解释一下方法调度的概念,它可以准确地模拟观察到的行为.
假设每个类都有一个哈希表,它将方法签名(名称和参数类型)映射到实际的代码块以实现该方法.当虚拟机尝试在实例上调用方法时,它会查询对象的类,并在类的表中查找请求的签名.如果找到方法体,则调用它.否则,获取该类的父类,并在那里重复查找.这一过程一直持续到找到方法,或者没有更多的父类 - 这会产生一个NoSuchMethodError
.
如果超类和子类在其表中都有相同方法签名的条目,则首先遇到子类的版本,并且永远不使用超类的版本 - 这是"覆盖".
现在,假设我们跳过对象实例,并从一个子类开始.分辨率可以如上所述进行,为您提供一种"可覆盖的"静态方法.然而,解决方案都可以在编译时发生,因为编译器是从已知类开始的,而不是等到运行时查询其类的未指定类型的对象."覆盖"静态方法没有意义,因为总是可以指定包含所需版本的类.
这里有一些材料可以解决最近编辑的问题.
听起来你想要为每个实现有效地强制使用类似构造函数的方法IXMLizable
.忘记尝试使用接口强制执行此操作一分钟,并假装您有一些符合此要求的类.你会怎么用?
class Foo implements IXMLizable{ public static Foo newInstanceFromXML(Element e) { ... } } Foo obj = Foo.newInstanceFromXML(e);
由于Foo
在"构造"新对象时必须明确命名具体类型,因此编译器可以验证它确实具有必要的工厂方法.如果没有,那么呢?如果我可以实现IXMLizable
缺少"构造函数"的东西,并创建一个实例并将其传递给您的代码,那么它就是一个IXMLizable
具有所有必要接口的东西.
构造是实现的一部分,而不是接口.任何与接口一起成功运行的代码都不关心构造函数.任何关心构造函数的代码都需要知道具体类型,并且可以忽略该接口.
这已经被问到并回答了
要复制我的答案:
在接口中声明静态方法永远不会有意义.它们不能通过正常调用MyInterface.staticMethod()执行.如果通过指定实现类MyImplementor.staticMethod()来调用它们,那么您必须知道实际的类,因此接口是否包含它是无关紧要的.
更重要的是,静态方法永远不会被覆盖,如果你尝试这样做:
MyInterface var = new MyImplementingClass(); var.staticMethod();
静态规则说必须执行在声明的var类型中定义的方法.由于这是一个界面,这是不可能的.
您无法执行"result = MyInterface.staticMethod()"的原因是它必须执行MyInterface中定义的方法的版本.但MyInterface中不能定义版本,因为它是一个接口.根据定义,它没有代码.
虽然你可以说这相当于"因为Java就是这样做的",但实际上这个决定是其他设计决策的逻辑结果,也是出于非常好的理由.
通常这是使用工厂模式完成的
public interface IXMLizableFactory{ public T newInstanceFromXML(Element e); } public interface IXMLizable { public Element toXMLElement(); }
随着Java 8的出现,现在可以在接口中编写默认和静态方法. docs.oracle/staticMethod
例如:
public interface Arithmetic {
public int add(int a, int b);
public static int multiply(int a, int b) {
return a * b;
}
}
public class ArithmaticImplementation implements Arithmetic {
@Override
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
int result = Arithmetic.multiply(2, 3);
System.out.println(result);
}
}
结果:6
提示:调用静态接口方法不需要由任何类实现.当然,这是因为超类中静态方法的相同规则适用于接口上的静态方法.
因为静态方法不能在子类中重写,因此它们不能是抽象的.接口中的所有方法事实上都是抽象的.
为什么我不能在Java接口中定义静态方法?
实际上你可以在Java 8中.
根据Java doc:
静态方法是一种与定义它的类相关联的方法,而不是与任何对象相关联的方法.该类的每个实例都共享其静态方法
在Java 8中,接口可以具有默认方法和静态方法.这使我们更容易在库中组织辅助方法.我们可以在同一个接口中保留特定于接口的静态方法,而不是在单独的类中.
list.sort(ordering);
代替
Collections.sort(list, ordering);
public interface TimeClient { // ... static public ZoneId getZoneId (String zoneString) { try { return ZoneId.of(zoneString); } catch (DateTimeException e) { System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead."); return ZoneId.systemDefault(); } } default public ZonedDateTime getZonedDateTime(String zoneString) { return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)); } }
接口与多态性有关,多态性本质上与对象实例有关,而与类无关.因此,静态在接口的上下文中没有意义.
首先,所有语言决策都是语言创建者做出的决定.在软件工程或语言定义或编译/解释器编写领域,没有任何内容表明静态方法不能成为接口的一部分.我为他们创建了几种语言和编写的编译器 - 它们只是坐下来定义有意义的语义.我认为接口中静态方法的语义非常清晰 - 即使编译器必须将方法的解析推迟到运行时.
其次,我们一直使用静态方法有一个有效的理由,有一个包含静态方法的接口模式 - 我不能代表任何人,但我定期使用静态方法.
最可能的正确答案是,在定义语言时,没有感觉到需要接口中的静态方法.多年来Java已经发展了很多,这个项目显然已经获得了一些兴趣.对Java 7进行了研究表明它已经上升到可能导致语言变化的兴趣水平.当我不再需要实例化一个对象时,我会很高兴我可以调用我的非静态getter方法来访问子类实例中的静态变量...
静态方法不像实例方法那样虚拟,所以我认为Java设计者认为他们不希望它们在接口中.
但是您可以将包含静态方法的类放在接口中.你可以试试!
public interface Test { static class Inner { public static Object get() { return 0; } } }
"是否有一个特殊原因导致静态方法无法被覆盖".
让我通过填写定义为你重新提出这个问题.
"在编译时解析的方法是否有特殊原因无法在运行时解析."
或者,更完整地说,如果我想调用没有实例的方法,但是知道类,我怎样才能根据我没有的实例来解析它.