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

# EYZ0 # EYZ1

下面是例子:

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

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

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

IXMLizable.newInstanceFromXML(e);

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

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

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

我设计的问题是我试图使用接口来强制编码约定。

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

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

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

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

409894 次浏览

因为静态方法不能在子类中重写,因此它们不能是抽象的。接口中的所有方法都是抽象的,事实上的

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

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

希望有帮助!

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

对于泛型,它们可以使用——无论是否使用默认实现。显然需要重写等等。然而,我的猜测是,这样的用法不是很面向对象(正如其他答案迟钝地指出的那样),因此被认为不值得付出努力来有效地实现它们。

接口关注的是多态性,它本质上是绑定到对象实例的,而不是类。因此,静态在接口上下文中没有意义。

不能在接口中定义静态方法,因为静态方法属于类而不是类的实例,而接口不是类。# EYZ0

然而,如果你愿意,你可以这样做:

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


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

在这种情况下,你有两个类,两个不同的静态方法称为methodX()。

这个问题已经被问过了,在这里

重复我的回答:

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

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

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

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

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

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

Java 8允许静态接口方法

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

这里有两个问题:

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

接口中的静态方法

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

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

覆盖静态方法

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

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

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

假设每个类都有一个哈希表,将方法签名(名称和参数类型)映射到实现方法的实际代码块。当虚拟机尝试在实例上调用方法时,它会查询对象中的类,并在类的表中查找请求的签名。如果找到方法体,就调用它。否则,将获得类的父类,并在那里重复查找。这将继续进行,直到找到方法,或者没有更多的父类—

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

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


构造函数“接口”

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

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

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


Foo obj = Foo.newInstanceFromXML(e);

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

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

静态方法不像实例方法那样是虚拟的,所以我猜想Java设计者决定不让它们出现在接口中。

但是您可以将包含静态方法的类放在接口中。你可以试试!

public interface Test {
static class Inner {
public static Object get() {
return 0;
}
}
}
我认为java没有静态接口方法,因为你不需要它们。你可能认为你知道,但是… 你会如何使用它们?如果你想叫它们

MyImplClass.myMethod()

那么就不需要在接口中声明它了。如果你想叫他们

myInstance.myMethod()

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

接口允许您在实现接口的类实例和调用代码之间定义契约。java帮助您确保这个契约没有被违反,因此您可以依赖它,而不必担心哪个类实现了这个契约,只要“某个签署了契约的人”就足够了。在静态接口的情况下,你的代码

MyImplClass.myMethod()

不依赖于每个接口实现都有此方法的事实,因此不需要Java来帮助您确定使用它。

有几个答案讨论了可覆盖静态方法概念的问题。然而,有时你会遇到一种模式,它似乎正是你想要使用的。

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

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

通过ID加载Person

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

这是相当方便的,但它有它的问题;值得注意的是,静态方法的存在不能在接口中强制执行。接口中可覆盖的静态方法正是我们所需要的,只要它能以某种方式工作。

ejb通过拥有一个Home接口来解决这个问题;每个对象都知道如何找到它的Home,而Home包含“静态”方法。通过这种方式,可以根据需要重写“静态”方法,并且不会因为不应用于bean实例的方法而使正常(称为“Remote”)接口变得混乱。只需让普通接口指定一个“getHome()”方法。返回Home对象的一个实例(我认为它可能是一个单例对象),调用者可以执行影响所有Person对象的操作。

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

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

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

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

假设你能做到;想想这个例子:

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


}

通常这是使用工厂模式完成的

public interface IXMLizableFactory<T extends IXMLizable> {
public T newInstanceFromXML(Element e);
}


public interface IXMLizable {
public Element toXMLElement();
}

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

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

因此,要在接口中获得最接近静态方法的方法是使用非静态方法,然后不访问任何非静态实例成员。其他任何方式都不可能带来性能好处,因为没有办法静态链接(在编译时)ISomething.member()。我认为在接口中使用静态方法的唯一好处是,它不会输入(即忽略)隐式的“this”,因此不允许访问任何非静态实例成员。这将隐式声明不访问“This”的函数是不变的,甚至对于其包含的类不是只读的。但是在接口ISomething中声明“static”也会使试图使用ISomething.member()访问它的人感到困惑,这将导致编译器错误。我想,如果编译器错误有足够的解释性,它会比试图教育人们使用非静态方法来完成他们想要的(显然主要是工厂方法)要好得多,就像我们在这里所做的(在这个网站上已经重复了3次),所以这显然是一个对许多人来说不是直观的问题。我不得不思考了一段时间才能得到正确的理解。

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

首先,所有的语言决策都是由语言创造者做出的决策。在软件工程、语言定义或编译器/解释器编写的世界中,没有任何东西说静态方法不能成为接口的一部分。我已经创建了两种语言,并为它们编写了编译器——这一切都只是坐下来定义有意义的语义。我认为接口中静态方法的语义非常清楚——即使编译器不得不将方法的解析推迟到运行时。

其次,我们使用静态方法意味着有一个包含静态方法的接口模式的正当理由——我不能代表你们,但我经常使用静态方法。

最可能的正确答案是,在定义该语言的时候,没有人认为接口中需要静态方法。Java在过去几年里发展了很多,这是一个显然引起了一些兴趣的项目。针对Java 7的研究表明,它已经上升到可能导致语言更改的兴趣级别。对于一个人来说,当我不再需要实例化一个对象,这样我就可以调用我的非静态getter方法来访问子类实例中的静态变量时,我会很高兴……

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

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

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

可以实现的是静态接口(而不是接口中的静态方法)。实现给定静态接口的所有类都应该实现相应的静态方法。你可以从任何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静态方法实现! 动态分派是必要的,您可以通过扩展类(当通过静态接口调用时)来重写类的静态方法(如果不是final方法)。 显然,这些方法只能访问它们类的静态变量

随着Java 8的出现,现在可以在接口中编写默认的静态方法。 # EYZ0 < / p >

例如:

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

# eyz0: 6

调用静态接口方法不需要由任何类实现。当然,这是因为适用于超类中的静态方法的规则同样适用于接口上的静态方法。

虽然我意识到Java 8解决了这个问题,但我认为我应该加入我目前正在研究的一个场景(锁定在使用Java 7),在这个场景中,能够在接口中指定静态方法将会很有帮助。

我有几个枚举定义,其中我定义了“id”和“displayName”字段以及helper方法,用于评估各种原因的值。实现接口使我能够确保getter方法到位,而不是静态帮助器方法。作为枚举,确实没有一种干净的方法可以将helper方法卸载到继承的抽象类或类似的东西中,因此方法必须在枚举本身中定义。另外,因为它是一个枚举,所以您永远无法将它作为实例对象传递,并将其视为接口类型,但能够要求通过接口存在静态帮助器方法是我喜欢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 8中可以。

按照Java 医生:

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

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

默认方法示例:

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));
}
}
让我们假设在接口中允许静态方法: *它们将强制所有实现类声明该方法。 *接口通常是通过对象来使用的,所以唯一有效的方法是非静态方法。 *任何知道特定接口的类都可以调用它的静态方法。因此,实现类的静态方法将在下面被调用,但调用方类不知道是哪个。怎么知道呢?它没有实例化来猜测!< / p >

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

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

元类就是为此目的服务的。你可以试试Java的class。但问题是Java在这方面不够灵活。不能在接口的类对象中声明方法。

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

..等等等等

不管怎样,你有一个简单的解决方法——用相同的逻辑使方法非静态。但是,您必须首先创建一个对象来调用该方法。

评论# EYZ0

这是正确的,静态方法自Java 8以来就被允许在接口中,但您的示例仍然不能工作。你不能只是定义一个静态方法:你必须实现它,否则你会得到一个编译错误。

要解决这个问题: 错误:缺少方法主体,或声明摘要

. 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();
}


}
< p >输出: 20 < / p >

现在我们可以在接口中使用静态方法