当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么我不能在Java接口中定义静态方法?》经验,为你挑选了10个好方法。

这是一个例子:

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

当然这不会奏效.但为什么不呢?

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

IXMLizable.newInstanceFromXML(e);

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

编辑:我想我正在寻找比"因为这就是Java的方式"更深层次的答案.

是否有一个特殊的技术原因导致静态方法无法被覆盖?也就是说,为什么Java的设计者决定使实例方法可覆盖但不是静态方法?

编辑:我的设计的问题是我正在尝试使用接口来强制执行编码约定.

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

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

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

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

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



1> erickson..:
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具有所有必要接口的东西.

构造是实现的一部分,而不是接口.任何与接口一起成功运行的代码都不关心构造函数.任何关心构造函数的代码都需要知道具体类型,并且可以忽略该接口.


#1的原因可能是多重继承吗?由于我们可以从多个接口继承,如果两个接口包含相同的静态方法签名,然后一个类同时实现它们并调用该方法,那么事情可能会变得复杂,Java语言创建者希望通过禁止多个类继承来避免第一名.显然,对于不允许在其中进行任何方法定义的接口,可以进行相同的参数.
“构造是实现的一部分,而不是接口的一部分。任何与接口成功工作的代码都不会在意构造函数。” -这显然是不正确的。在其他语言中(例如Swift),我可以创建T的新实例而无需静态地知道T,因为我保证在接口中某个特定的构造函数(或静态方法)将在运行时存在。Java无法指定生成这一事实,并不意味着它不是有意义的事情。

2> DJClayworth..:

这已经被问到并回答了

要复制我的答案:

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

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

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

静态规则说必须执行在声明的var类型中定义的方法.由于这是一个界面,这是不可能的.

您无法执行"result = MyInterface.staticMethod()"的原因是它必须执行MyInterface中定义的方法的版本.但MyInterface中不能定义版本,因为它是一个接口.根据定义,它没有代码.

虽然你可以说这相当于"因为Java就是这样做的",但实际上这个决定是其他设计决策的逻辑结果,也是出于非常好的理由.


如果你使用作为泛型类型参数,那么保证通过接口T可以.doSomething()是很好的.
虽然我理解这些论点,但我同意@Chris_Betti(即使对于非泛型类型):代码结构确保某些类*实现*特定的静态API会很好.也许有可能使用不同的概念......
这似乎是不真实的:"在接口中声明静态方法永远不会有任何意义." 如果我有一个类的集合,没有实例化,可以提供一些信息,但我需要一个通用的接口来放置这个静态类级信息(即一个带有可覆盖的静态方法的接口)然后这是一个有效的用途.想想反思++,你可以捕获关于类属性的元信息,而不用乱砍属性,反射等.

3> Peter Lawrey..:

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

public interface IXMLizableFactory {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}


+1工厂模式听起来像问题的解决方案.(虽然不是问题)

4> rogue lad..:

随着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

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



5> Michael Myer..:

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


走出自己并回答问题:*为什么*不能覆盖静态方法?如果可以覆盖静态方法,它会是什么样子?你能用它做什么?这个答案基本上是"你不能,因为你不能."
您始终可以强制每种类型实现任何静态接口方法.Typeclasses,有人吗?

6> i_am_zero..:

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

实际上你可以在Java 8中.

根据Java doc:

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

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



7> cliff.meyers..:

接口与多态性有关,多态性本质上与对象实例有关,而与类无关.因此,静态在接口的上下文中没有意义.



8> 小智..:

首先,所有语言决策都是语言创建者做出的决定.在软件工程或语言定义或编译/解释器编写领域,没有任何内容表明静态方法不能成为接口的一部分.我为他们创建了几种语言和编写的编译器 - 它们只是坐下来定义有意义的语义.我认为接口中静态方法的语义非常清晰 - 即使编译器必须将方法的解析推迟到运行时.

其次,我们一直使用静态方法有一个有效的理由,有一个包含静态方法的接口模式 - 我不能代表任何人,但我定期使用静态方法.

最可能的正确答案是,在定义语言时,没有感觉到需要接口中的静态方法.多年来Java已经发展了很多,这个项目显然已经获得了一些兴趣.对Java 7进行了研究表明它已经上升到可能导致语言变化的兴趣水平.当我不再需要实例化一个对象时,我会很高兴我可以调用我的非静态getter方法来访问子类实例中的静态变量...



9> Adrian Pronk..:

静态方法不像实例方法那样虚拟,所以我认为Java设计者认为他们不希望它们在接口中.

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

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}



10> Darron..:

"是否有一个特殊原因导致静态方法无法被覆盖".

让我通过填写定义为你重新提出这个问题.

"在编译时解析的方法是否有特殊原因无法在运行时解析."

或者,更完整地说,如果我想调用没有实例的方法,但是知道类,我怎样才能根据我没有的实例来解析它.

推荐阅读
手机用户2402852387
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有