为什么我不能在Java界面中定义静态方法?

这是一个例子:

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

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

其中一个可能的问题是,当你打电话时会发生什么:

 IXMLizable.newInstanceFromXML(e); 

在这种情况下,我认为它应该只是调用一个空的方法(即{})。 所有的子类将被迫实现静态方法,所以在调用静态方法时,它们都会很好。 那为什么不可能呢?

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

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

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

也就是说,界面的目标是双重的:

  1. 我希望IXMLizable接口允许我将实现它的类转换为XML元素(使用多态性,工作正常)。

  2. 如果有人想要创建一个实现了IXMLizable接口的类的新实例,他们总是会知道会有一个newInstanceFromXML(Element e)静态构造函数。

有没有其他的方法来确保这一点,除了在界面中留言?

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

Java 8允许使用静态接口方法

使用Java 8,接口可以有静态方法。 他们也可以有具体的实例方法,但不包含实例字段。

这里真的有两个问题:

  1. 为什么在过去不好的时候,接口不能包含静态方法?
  2. 为什么不能重写静态方法?

接口中的静态方法

以前的版本中没有强大的技术原因,为什么一个接口不能有静态方法。 这是由一个重复问题的海报很好地总结 。 静态接口方法最初被认为是一种小的语言变化,然后有一个正式的建议 ,将其添加到Java 7中,但后来由于不可预见的复杂性而被放弃。

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

重写静态方法

第二个问题的答案稍微复杂一些。

静态方法在编译时可以解析。 动态调度对于实例方法是有意义的,因为编译器无法确定对象的具体类型,因此无法解析要调用的方法。 但是调用一个静态方法需要一个类,而且由于这个类是静态的 – 在编译时 – 动态调度是不必要的。

了解实例方法如何工作的一点背景知道这里发生了什么。 我相信实际的实现是完全不同的,但是让我解释一下我的方法调度的概念,它准确地模拟观察到的行为。

假设每个类都有一个散列表,它将方法签名(名称和参数类型)映射到实际的代码块来实现该方法。 当虚拟机试图调用实例上的方法时,它会查询对象的类,并在类的表中查找所请求的签名。 如果找到方法体,则调用它。 否则,获得该类的父类,并在那里重复查找。 直到找到该方法,或者没有更多的父类 – 这会导致NoSuchMethodError

如果一个超级类和一个子类在它们的表中都有一个相同方法签名的入口,则首先遇到子类的版本,超类的版本从不使用 – 这是一个“覆盖”。

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


构造函数“接口”

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

这听起来像是你想有效地为IXMLizable每个实现IXMLizable一个构造函数的方法。 忘记试图用接口强制执行一分钟,假装你有一些类可以满足这个要求。 你将如何使用它?

 class Foo implements IXMLizable<Foo> { 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<T extends IXMLizable> { 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 文档 :

静态方法是一种与其定义的类关联的方法,而不是任何对象。 每个类的实例共享其静态方法

在Java 8中,接口可以有默认方法静态方法 。 这使我们更容易在我们的图书馆中组织帮助方法。 我们可以在同一个接口中保留特定于接口的静态方法,而不是单独的类。

默认方法示例:

 list.sort(ordering); 

代替

 Collections.sort(list, ordering); 

静态方法的示例(来自doc本身):

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

那么,没有泛型,静态接口是无用的,因为所有静态方法调用在编译时解析。 所以,他们没有真正的用处。

有了泛型,他们有或没有默认的实现。 显然这需要重写等等。 然而,我的猜测是,这样的用法不是很OO(因为其他的答案都是指出来的),因此被认为是不值得的,他们需要付出的努力才能有效地实现。

几个答案已经讨论了可重写的静态方法的概念的问题。 不过有时你会遇到一种模式,看起来就像你想要的那样。

例如,我使用具有值对象的对象关系图层,但也具有操作值对象的命令。 由于各种原因,每个值对象类都必须定义一些静态方法,让框架找到命令实例。 例如,要创建一个人,你会做:

 cmd = createCmd(Person.getCreateCmdId()); Person p = cmd.execute(); 

并通过你的ID加载一个人

 cmd = createCmd(Person.getGetCmdId()); cmd.set(ID, id); Person p = cmd.execute(); 

这是相当方便的,但是它有问题; 特别是静态方法的存在不能在接口中强制执行。 接口中的一个可重写的静态方法正是我们所需要的,只要它能以某种方式工作。

EJB通过一个Home接口来解决这个问题; 每个对象知道如何找到它的家庭和家庭包含“静态”的方法。 这样,“静态”方法可以根据需要重写,而且不会将不适用于bean实例的方法混淆在正常的(称为“远程”)接口上。 只要使正常的接口指定一个“getHome()”方法。 返回Home对象的实例(可能是单例,我想),调用者可以执行影响所有Person对象的操作。

 Why can't I define a static method in a Java interface? 

接口中的所有方法都是明确抽象的,因此不能将它们定义为静态方法,因为静态方法不能是抽象的。

一个接口永远不能被静态解引用,例如ISomething.member 。 一个接口总是通过一个引用接口的子类实例的变量来解引用。 因此,一个接口引用无法知道它引用的是哪个子类而没有它的子类的一个实例。

因此,接口中最接近静态方法的方法是忽略“this”的非静态方法,即不访问实例的任何非静态成员。 在低级抽象中,每个非静态方法(在任何vtable中查找之后)实际上只是一个带有“this”作为隐式形式参数的类作用域的函数。 请参阅Scala的单例对象和与Java的互操作性,作为该概念的证据。 因此,每个静态方法都是一个不带“this”参数的类作用域的函数。 因此通常静态方法可以被静态调用,但是如前所述,一个接口没有实现(是抽象的)。

因此,要在一个接口中获得最接近静态方法的是使用非静态方法,那么不要访问任何非静态实例成员。 没有其他方式可能的性能优势,因为无法静态链接(在编译时)一个ISomething.member() 。 我在接口中看到的静态方法的唯一好处是它不会输入(即忽略)隐式“this”,因此不允许访问任何非静态实例成员。 这将隐含地声明,不能访问“this”的函数是不变的,甚至不能读取它所包含的类。 但是,在接口ISomething声明“静态”也会将试图访问它的人与ISomething.member()混淆,这会导致编译器错误。 我想如果编译器的错误是足够的解释性的,那就好比试图教育人们使用非静态方法来完成他们想要的东西(显然主要是工厂方法),正如我们在这里所做的(并且重复了3次问答时间在这个网站上),所以这显然是许多人不直观的问题。 为了得到正确的理解,我不得不考虑一段时间。

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

EDIT: As of Java 8, static methods are now allowed in interfaces.注释EDIT: As of Java 8, static methods are now allowed in interfaces.

这是正确的,因为Java 8中的静态方法在接口中是允许的,但是你的例子仍然不起作用。 你不能只是定义一个静态方法:你必须实现它,否则你会得到一个编译错误。

接口只是提供一个类将提供的东西的列表,而不是这些东西的实际实现,这是你的静态项目。

如果你需要静态,使用一个抽象类并继承它,否则,删除静态。

希望有所帮助!

您不能在接口中定义静态方法,因为静态方法不属于类的实例,接口也不属于类。 在这里阅读更多。

但是,如果你想要,你可以这样做:

 public class A { public static void methodX() { } } public class B extends A { public static void methodX() { } } 

在这种情况下,你所拥有的是两个具有2个不同的静态方法的类,分别称为methodX()。

假设你可以做到这一点, 考虑这个例子:

 interface Iface { public static void thisIsTheMethod(); } class A implements Iface { public static void thisIsTheMethod(){ system.out.print("I'm class A"); } } class B extends Class A { public static void thisIsTheMethod(){ System.out.print("I'm class B"); } } SomeClass { void doStuff(Iface face) { IFace.thisIsTheMethod(); // now what would/could/should happen here. } } 

可以实现的东西是静态接口(而不是接口中的静态方法)。 所有实现给定静态接口的类都应该实现相应的静态方法。 你可以从任何Class clazz中使用静态接口SI

 SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI // alternatively if the class is known at compile time SI si = Someclass.static.SI; // either compiler errror or not null 

那么你可以调用si.method(params) 。 这将是有用的(以工厂设计模式为例),因为您可以从编译时未知的类获得(或检查执行)SI静态方法实现! 动态分派是必要的,你可以通过扩展它(当通过静态接口调用时)来覆盖类的静态方法(如果不是最终的)。 显然,这些方法只能访问其类的静态变量。

虽然我意识到Java 8解决了这个问题,但是我还是想用我正在使用的一个场景(使用Java 7进行锁定),在那里能够在接口中指定静态方法会有所帮助。

我有几个枚举定义,我已经定义了“id”和“displayName”字段以及由于各种原因评估值的帮助器方法。 实现一个接口允许我确保getter方法就位,而不是静态帮助器方法。 作为一个枚举,真的没有一个简单的方法来卸载辅助方法到一个继承的抽象类或类似的东西,所以方法必须在枚举本身定义。 也因为它是一个枚举,你永远不可能实际上将它作为一个实例对象传递,并将其作为接口类型,但能够通过接口要求存在静态帮助器方法是我喜欢的它在Java 8中得到了支持。

这是代表我的观点的代码。

接口定义:

 public interface IGenericEnum <T extends Enum<T>> { String getId(); String getDisplayName(); //If I was using Java 8 static helper methods would go here } 

一个枚举定义的例子:

 public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> { STANDARD ("Standard", "Standard Mode"), DEBUG ("Debug", "Debug Mode"); String id; String displayName; //Getter methods public String getId() { return id; } public String getDisplayName() { return displayName; } //Constructor private ExecutionModeType(String id, String displayName) { this.id = id; this.displayName = displayName; } //Helper methods - not enforced by Interface public static boolean isValidId(String id) { return GenericEnumUtility.isValidId(ExecutionModeType.class, id); } public static String printIdOptions(String delimiter){ return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter); } public static String[] getIdArray(){ return GenericEnumUtility.getIdArray(ExecutionModeType.class); } public static ExecutionModeType getById(String id) throws NoSuchObjectException { return GenericEnumUtility.getById(ExecutionModeType.class, id); } } 

泛型枚举实用程序定义:

 public class GenericEnumUtility { public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) { for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) { if(enumOption.getId().equals(id)) { return true; } } return false; } public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){ String ret = ""; delimiter = delimiter == null ? " " : delimiter; int i = 0; for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) { if(i == 0) { ret = enumOption.getId(); } else { ret += delimiter + enumOption.getId(); } i++; } return ret; } public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){ List<String> idValues = new ArrayList<String>(); for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) { idValues.add(enumOption.getId()); } return idValues.toArray(new String[idValues.size()]); } @SuppressWarnings("unchecked") public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException { id = id == null ? "" : id; for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) { if(id.equals(enumOption.getId())) { return (T)enumOption; } } throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , "))); } } 

让我们假设接口允许使用静态方法:*它们将强制所有实现类声明该方法。 *接口通常是通过对象来使用的,所以那些唯一有效的方法就是非静态方法。 任何知道特定接口的类都可以调用它的静态方法。 因此一个实现类的静态方法将在下面被调用,但是调用者类不知道哪一个。 如何知道呢? 它没有实例来猜测!

接口被认为是在处理对象时使用的。 这样,一个对象是从一个特定的类实例化,所以这个最后的事情解决了。 调用类不需要知道哪个特定的类,因为实例化可以由第三个类来完成。 所以调用类只知道接口。

如果我们想把它扩展到静态方法,我们应该有可能在之前指定一个实现类,然后传递一个对调用类的引用。 这可以通过接口中的静态方法使用该类。 但是这个引用和对象之间有什么区别呢? 我们只需要一个表示它是什么类的对象。 现在,对象代表了旧的类,并且可以实现一个新的接口,包括旧的静态方法 – 那些现在是非静态的。

元类用于此目的。 您可以尝试Java类。 但问题是Java对此不够灵活。 你不能在接口的类对象中声明一个方法。

这是一个元问题 – 当你需要做屁股

..blah等等

无论如何,你有一个简单的解决方法 – 使相同的逻辑方法非静态。 但是,你将不得不先创建一个对象来调用方法。

为了解决这个问题:error:缺少方法体,或者声明抽象static void main(String [] args);

 interface I { int x=20; void getValue(); static void main(String[] args){};//Put curly braces } class InterDemo implements I { public void getValue() { System.out.println(x); } public static void main(String[] args) { InterDemo i=new InterDemo(); i.getValue(); } } 

输出:20

现在我们可以在界面中使用静态方法了

我认为java没有静态接口方法,因为你不需要它们。 你可能会认为你这样做,但是…你将如何使用它们? 如果你想打电话给他们

 MyImplClass.myMethod() 

那么你不需要在界面中声明它。 如果你想打电话给他们

 myInstance.myMethod() 

那么它不应该是静态的。 如果你实际上是要使用第一种方式,但是只是想强制每个实现有这样的静态方法,那么它实际上是一个编码约定,而不是实现接口和调用代码的实例之间的契约。

接口允许你定义实现接口的类的实例和调用代码之间的契约。 而且java可以帮助你确定这个合同没有被违反,所以你可以依靠它,不用担心什么类实现了这个合同,只是“签了合同的人”就够了。 在静态接口的情况下你的代码

 MyImplClass.myMethod() 

不依赖于每个接口实现都有这个方法的事实,所以你不需要java来帮助你确定它。

What is the need of static method in interface, static methods are used basically when you don't have to create an instance of object whole idea of interface is to bring in OOP concepts with introduction of static method you're diverting from concept.