为什么接口中的所有字段都隐式地是静态的和最终的?


100

我只是想了解为什么接口中定义的所有字段都是隐式static和的final。保留字段的想法static对我来说很有意义,因为您无法拥有接口的对象,但是为什么final(隐式地)存在它们呢?

任何人知道为什么Java的设计者在接口使得场去staticfinal


给我自己一个说明:它是静态的,因为接口的字段不会成为实现它的对象的一部分。
下雨

Answers:


126

接口不能具有行为或状态,因为它仅用于指定交互协定,而不能指定实现细节。通过不允许方法/构造函数主体或静态/实例初始化块来强制执行“无行为”。仅允许静态最终字段强制实施“无状态”。因此,该类可以具有状态(静态),但是实例状态不能由接口推断出来。

顺便说一句:Java中的常量由静态的final字段定义(按照惯例,名称使用UPPER_CASE_AND_UNDERSCORES)。


54
最终字段不一定是常量;仅保证原始类型。通常,final关键字仅表示内存位置不会更改。
流行

8
我没有说final字段是常量,只是常量是final字段。注意,允许在接口中放置非基本静态final字段。即使该字段的内容可能会更改,对其的引用也保持不变。
Adriaan Koster 2012年

1
@AdriaanKoster您确切地说,final字段是常量:仅允许常量不会强制执行任何状态。-这句话暗示所有最终字段都是常数。您可能会尝试进一步争论所使用的词语,但显然您的陈述存在误导性。
托马什Zato -恢复莫妮卡

2
这肯定是我的智力下降了,但是在看了这个答案(正好是我得分最高的答案)的六年之后,我仍然不明白这句话。请提出其他措辞,因为我看不到任何错误。
Adriaan Koster

Java设计人员可能曾打算将接口设置为无状态,但由于实例字段可以是可修改的类,所以它们失败了。他们选择不将实例字段强制为static final,而不是承认它们失败,而是将实例字段强制为,这与const在Java中获得的逼近(真实C / C ++)一样。不幸的是,这是隐式的,并且可能导致非专家的困惑。(我只是意识到,这是static因为我观察到了意外的行为。我了解到,它们final仅来自此答案。)
不是用户

27

存在的原因 final

如果未将其定义为final,则任何实现都可以更改字段的值。然后它们将成为实现的一部分。接口是纯规范,没有任何实现。

存在的原因 static

如果它们是静态的,则它们属于接口,而不是对象,也不属于对象的运行时类型。


18

这里有几点要点:

仅仅因为接口中的字段是隐式静态的final并不意味着它们必须是编译时常量,甚至是不可变的。您可以定义例如

interface I {
  String TOKEN = SomeOtherClass.heavyComputation();
  JButton BAD_IDEA = new JButton("hello");
}

(请注意,在批注定义中执行此操作可能会使javac感到困惑,这与上述内容实际上编译为静态初始化程序有关。)

同样,这种限制的原因是风格上的,而不是技术上的,许多人希望看到它放松


9

这些字段必须是静态的,因为它们不能是抽象的(就像方法可以那样)。因为它们不能是抽象的,所以实现者将无法在逻辑上提供字段的不同实现。

我认为这些字段必须是最终字段,因为许多不同的实现者都可以访问这些字段,从而使它们可以更改可能会出现问题(因为同步)。还要避免它被重新实现(隐藏)。

只是我的想法。


NawMan,您对“菲尔兹必须是静态的……”的解释没有多大意义。但是您说的很对,“这些领域必须是最终的……”
peakit

1
对于字段必须为最终字段的原因,我认为他是不对的。允许不同的实现者更改字段是没有问题的,因为否则继承将是有问题的。正如Adriaan所说,字段必须是最终的,因为接口应该而且应该是无状态的。具有状态的接口基本上应该是抽象类。
Axelle Ziegler,2009年

如果您的public static字段不是final,则findbugs会抱怨(正确!)。
汤姆·哈特芬

2

我认为Java语言设计者对字段的最终限制是不适当的限制,并且是错误的。在某些情况下(例如树处理),您需要在实现中设置对接口类型的对象执行操作所需的常量。在实现类上选择代码路径是一件麻烦事。我使用的解决方法是定义接口函数并通过返回文字来实现它:

public interface iMine {
    String __ImplementationConstant();
    ...
}

public class AClass implements iMine {
    public String __ImplementationConstant(){
        return "AClass value for the Implementation Constant";
    }
    ...
}

public class BClass implements iMine {
    public String __ImplementationConstant(){
        return "BClass value for the Implementation Constant";
    }
    ...
}

但是,使用以下语法会更简单,更清晰并且更不容易出现异常实现:

public interface iMine {
    String __ImplementationConstant;
    ...
}

public class AClass implements iMine {
    public static String __ImplementationConstant =
        "AClass value for the Implementation Constant";
    ...
}

public class BClass implements iMine {
    public static String __ImplementationConstant =
        "BClass value for the Implementation Constant";
    ...
}

您似乎在抱怨字段是静态的而不是字段。
Daniel Yankowsky 2014年

0

规范,合同...用于字段访问的机器指令使用对象地址加字段​​偏移量。由于类可以实现许多接口,因此无法在扩展该接口的所有类中使非最终接口字段具有相同的偏移量。因此,必须实现不同的字段访问机制:两次内存访问(获取字段偏移量,获取字段值),而不是一次,并维护一种虚拟字段表(虚拟方法表的模拟)。猜猜他们只是不想让jvm复杂化,而可以通过现有的东西(方法)轻松地模拟这些功能。

在scala中,我们可以在接口中具有字段,尽管在内部它们是按照我上面解释的方式实现的(作为方法)。


-1

static

staticJava中的任何内容(变量或方法)都可以作为Classname.variablenameClassname.methodname或直接调用。不必仅通过使用对象名称来调用它。

在接口中,无法声明对象,并且static可以仅通过类名调用变量而无需对象名。

final

它有助于为变量保持恒定的值,因为它不能在其子类中被覆盖。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.