关于Java:接口常数的使用方法是什么?

What is the use of interface constants?

我正在学习Java,只是发现接口可以有字段,这些字段是公共静态的和最终的。到目前为止,我还没有看到这些例子。这些接口常数的一些用例是什么,我能在Java标准库中看到一些吗?


将静态成员放入接口(并实现该接口)是一个糟糕的做法,甚至还有一个名字,即常量接口反模式,见有效Java,项目17:

The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class's exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface.

There are several constant interfaces in the java platform libraries, such as java.io.ObjectStreamConstants. These interfaces should be regarded as anomalies and
should not be emulated.

为了避免常量接口的一些缺陷(因为您不能阻止人们实现它),最好使用具有私有构造函数的适当类(从维基百科中借用的示例):

1
2
3
4
5
6
7
8
9
public final class Constants {

    private Constants() {
        // restrict instantiation
    }

    public static final double PI = 3.14159;
    public static final double PLANCK_CONSTANT = 6.62606896e-34;
}

并且访问常量而不必完全限定它们(即不必用类名前缀它们),使用静态导入(因为Java 5):

1
2
3
4
5
6
7
8
9
import static Constants.PLANCK_CONSTANT;
import static Constants.PI;

public class Calculations {

    public double getReducedPlanckConstant() {
        return PLANCK_CONSTANT / (2 * PI);
    }
}


"The constant interface pattern is a poor use of interfaces"

Ok.

不管是谁编造了这个假设,不管他/她是什么大师,都是根据继续有效地执行坏习惯和实践的需要编造出来的。这一假设是基于对软件设计不良习惯有效性的提升。好的。

我在这里写了一个反对这种假设的回应:在Java中实现常数的最佳方法是什么?解释这个假设的无根据性。好的。

10年来,这个问题一直是开放的,直到它在我发表我的理由证明这个假设是正确的后2小时内被关闭,从而暴露出那些紧紧抓住这个误导性假设的人不愿意辩论。好的。

这些是我对这个假设所表达的观点好的。

  • 持有这一假设的基础是需要方法和限制性规则来应对不良软件习惯和方法的影响。好的。

  • 这种观点的支持者"持续的界面模式是对界面的不良使用",除了那些需要应对这些不良习惯和做法的影响所导致的原因外,无法提供其他任何原因。好的。

  • 解决根本问题。好的。

  • 那么,为什么不充分利用和开发Java语言结构的每一个语言特征,以方便自己。不需要夹克。为什么要发明一些规则来阻止你无效的生活方式来歧视和指控更有效的生活方式?好的。

根本问题

是信息组织。在设计或补充流程的解决方案之前,首先应该了解中介流程的信息以及该信息的行为,以及所谓的业务规则。这种信息组织方法几十年前被称为数据规范化。好的。

因此,只有解决方案的工程化才是可能的,因为将解决方案组件的粒度和模块性与信息组件的粒度和模块性相匹配是最佳策略。好的。

组织信息有两到三个重要障碍。好的。

  • 缺乏对数据模型"规范化"需求的认识。好的。

  • ef-codd关于数据规范化的声明存在缺陷、缺陷和歧义。好的。

  • 伪装成敏捷工程的最新时尚是错误的概念,即一个人不应该预先计划和调整模块的组织,因为你可以随时随地重构。重构和不受未来发现阻碍的持续变化被用作借口。然后,通过使用会计技巧延迟利润和资产化,对过程信息行为的基本发现,因此,现在认为不需要必要的基本知识及其处理。好的。

  • 使用接口常量是很好的实践。

    不要仅仅因为你喜欢你的即席点击和运行编程习惯而制定规则或发布任何反其道而行之的FATWA。好的。

    不要以不知道如何操作枪支或容易滥用枪支为由禁止枪支所有权。好的。

    如果你编造的规则是为那些不能专业编码的新手设计的,并且你把自己算在其中,那么说吧——不要把你的fatwa声明为适用于适当规范化的数据模型。好的。一个愚蠢的推理——接口不是由Java语言的FLANDER使用的吗?

    我不在乎开国元勋对美国宪法的初衷。我不在乎那些不成文的未经修改的意图。我只关心成文宪法中的文字编纂内容,以及如何利用它们实现社会的有效运作。好的。

    我只关心Java语言/平台规范允许我做什么,我打算充分利用它们来为我提供一个高效而有效地表达我的软件解决方案的媒介。不需要夹克。好的。使用枚举常量实际上是可怕的做法。

    它需要编写额外的代码来将参数映射到值。Java的创建者没有提供参数值映射,而不需要编写映射代码来演示EnUM常量,就像Java语言的无意使用一样。好的。

    特别是,由于不鼓励您规范化和组件化参数,所以会有一种错误的印象,即混合到枚举包中的参数属于同一维度。好的。常量是API协定

    别忘了。如果您设计和规范化了数据模型,并且它们包含常量,那么这些常量就是契约。如果您没有规范化您的数据模型,那么您应该遵守关于如何实践限制性编码来处理这个坏习惯的FAT。好的。

    因此,接口是实现常量契约的完美方式。好的。一个奇怪的假设——如果不小心实现了接口会怎么样?

    是的。任何人都可能无意中实现任何接口。没有什么能阻挡这种不经意间的程序员。好的。设计和规范化数据模型以防泄漏

    不要制定限制性法令来保护导致未控制/杂散参数泄漏到API中的假定不良行为。解决基本问题,而不是将责任归咎于接口常量。好的。不使用IDE是不好的做法

    一个正常工作和有效的程序员不需要证明她能在水下呆多久,在酷热或潮湿的雷雨中能走多远。她每天要用一个有效的工具,比如汽车或公共汽车,或者至少一辆自行车,载她走10英里去上班。好的。

    不要仅仅因为你对无IDE编程有一种深奥的禁欲主义的痴迷而限制其他程序员。好的。设计了几个框架来帮助程序员有效地继续实践坏习惯。

    OSGi就是这样一个框架。反对界面常数的法令也是如此。好的。因此,结论性的答案…

    接口常量是一种有效的方法,可以将其放入数据模型设计良好且规范化的契约组件中。好的。

    嵌套在类文件中的适当命名的私有接口中的接口常量也是一种很好的做法,可以将所有私有常量分组,而不是将它们分散到文件中。好的。好啊。


    Joshua Bloch:"有效的Java编程语言指南":

    The constant interface pattern is a poor use of interfaces. That a
    class uses some constants internally is an implementation detail.
    Implementing a constant interface causes this implementation detail to
    leak into the class's exported API. It is of no consequence to the
    users of a class that the class implements a constant interface. In
    fact, it may even confuse them. Worse, it represents a commitment: if
    in a future release the class is modified so that it no longer needs
    to use the constants, it still must implement the interface to ensure
    binary compatibility. If a nonfinal class implements a constant
    interface, all of its subclasses will have their namespaces polluted
    by the constants in the interface.


    如果您有将在实现接口的类中使用的公共常量,那么它们是有用的。

    下面是一个例子:http://www.javapractices.com/topic/topication.do?ID=32

    但请注意,建议的做法是在接口中使用静态导入而不是常量。以下是参考资料:http://www.javapractices.com/topic/topicaction.do?ID=195


    有一些答案是非常合理的。

    但我对这个问题有一些想法。(可能是错误的)

    在我看来,一个接口中的字段不应该是整个项目的常量,它们只是接口的手段,接口扩展它,以及实现这些接口或与它们有密切关系的类。它们应该在一定范围内使用,而不是在全局范围内使用。


    关于接口的两点:

    • 接口描述了实现它的对象可以做什么的子集。(这是直觉)

    • 接口描述了公共常量,后面跟着实现它的对象。

      • 这些公共常量是为了让客户机了解更多关于这些对象的信息。
      • 因此常量接口对于定义全局常量确实是反直觉的,因为接口用于描述一些对象,而不是所有对象/没有对象(考虑全局的含义)。

    所以我认为如果常量接口不用于全局常量,那么它是可以接受的:

    • 如果这些公共常量有好的名称,它们将建议实现者使用它们(下面的示例参见我的第一点)。
    • 如果一个类想要按照规范与这些类同步,只需implements它(当然,在实现中使用这些公共常量)。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    interface Drawable {

        double GOLDEN_RATIO = 1.618033988;
        double PI = 3.141592653;
        ...

        // methods
        ...
    }

    public class Circle implements Drawable {
        ...
        public double getCircumference() {
            return 2 * PI * r;
        }
    }

    void usage() {

        Circle circle = new Circle(radius: 3.0);
        double maxRadius = 5.0;

        if ( circle.getCircumference() < 2 * Circle.PI * maxRadius ) {
            ...
        }

    }

    在本例中:

    • Circle implements Drawable可以看出,Circle很可能与Drawable中定义的常量一致,否则,由于已经取了好的PIGOLDEN_RATIO两个名称,他们不得不选择一个更差的名称!
    • 只有这些Drawable对象符合Drawable中定义的特定PIGOLDEN_RATIO,才能有不属于Drawable的对象,具有不同的pi和golden ratio精度。


    我现在碰到这个老问题好几次了,被接受的答案仍然让我困惑。经过多次思考,我认为这个问题可以进一步澄清。

    在什么情况下这是一个坏做法?

    我认为@pascal thevent的答案强调错误,以下是我的版本:

    Putting static members into an interface (and implementing that interface) is a bad practice.

    有效Java的引用假设了其他人正在执行的恒定接口,我认为不应该(也不会)发生。

    当您创建一个名为Constants之类的常量接口时,世界上谁会实现它?(尽管技术上可行,但这是唯一的问题)

    在标准库中不会发生这种情况

    标准库不能承受任何可能的设计误用,所以您将看不到任何设计。

    但是,对于普通开发人员的日常项目来说,使用常量接口要容易得多,因为您不必担心staticfinalempty constructor等,也不会造成任何不良的设计问题。我能想到的唯一缺点是它仍然有"interface"的名称,但仅此而已。


    我遇到这个问题,想补充一些没有提到的内容。一般来说,我同意帕斯卡的回答。然而,我不认为接口上的常量"总是"是反模式。

    例如,如果您定义的常量是合同的一部分,那个接口,我认为对于常量来说,这个接口是一个很好的地方。在某些情况下,在不向实现的用户公开合同的情况下私下验证参数是不合适的。如果没有面向公众的契约,用户只能猜测您的验证是什么,而不需要解压类和阅读代码。

    因此,如果您实现了一个接口,并且该接口具有用于确保协定的常量(例如整数范围),那么您的类的用户可以通过检查接口本身的常量来确保他们正确地使用接口实例。如果常量对您的实现或实现是私有的,那么这是不可能的。包是私有的还是其他的。


    javax.swing.SwingConstants接口是一个例子,它获得了在Swing类之间使用的静态字段。这样你就可以很容易地使用

    • this.add(LINE_START, swingcomponent);
    • this.add(this.LINE_START, swingcomponent);
    • this.add(SwingComponents.LINE_START, swingcomponent);

    但是这个接口没有方法…


    我在处理类之间的共享常量时使用接口常量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public interface TestConstants
    {
        String RootLevelConstant1 ="RootLevelConstant1";

        interface SubGroup1
        {
            String SubGroupConstant1 ="SubGroup1Constant1";
            String SubGroupConstant2 ="SubGroup1Constant2";
        }

        interface SubGroup2
        {
            String SubGroupConstant1 ="SubGroup2Constant1";
            String SubGroupConstant2 ="SubGroup2Constant2";
        }
    }

    分组是一项巨大的资产,尤其是具有一组大型常量的资产。

    要使用,只需将它们链接在一起:

    1
    2
    3
    System.out.println(TestConstants.SubGroup1.SubGroupConstant1);
    System.out.println(TestConstants.SubGroup2.SubGroupConstant1);
    System.out.println(TestConstants.RootLevelConstant1);


    fields should be declared in an interface so that they are easier to
    share and can be referenced without introducing extra coupling.

    来源:Java开发工具编码风格