【问题标题】:Should a collection of constants be placed in a class or interface?常量集合应该放在类或接口中吗?
【发布时间】:2009-09-03 11:59:53
【问题描述】:

如果我想集中声明一组静态常量,以便在将它们放在类或接口 (Java) 中时可以在各种项目之间共享它们。

过去我看到它们大多放在一个类中,但我开始认为,由于该类不会也不应该被实例化,也许它们在接口中会更好,但是接口不应该由任何人实现类,例如

public class ErrorCodes {
    public static final String ERROR_1 = "-1";
    public static final String ERROR_2 = "-2";
}

public interface ErrorCodes {
    public static final String ERROR_1 = "-1";
    public static final String ERROR_2 = "-2";
}

【问题讨论】:

    标签: java standards coding-style


    【解决方案1】:

    如果他们有很强的联系,那么我会把他们放在一个枚举中:

    public enum Error {
      ERROR_1("-1", "foo went wrong"),
      ERROR_2("-2", "bar went wrong");
    
      private final String id;
      private final String message;
    
      Error(String id, String message) {
        this.id=id;
        this.message=message;
      }
    
      public String getId() {
        return id;
      }
    
      public String getMessage() {
        return message;
      }
    }
    

    优点是您可以在代码中实现类型安全,并且可以轻松添加基于 id 的查找(通过在构造函数中构建 HashMap<String,Error> 或通过简单地循环 values())。

    【讨论】:

    • 枚举实际上是一种更好的方法。我想我没有仔细阅读他的问题以理解他的意图......
    • 我发现这是最好的解决方案,因为您可以将数字和字符串放在一起并从属性文件中加载字符串。一切都在一个地方。
    • 如果您有大量常量,这仍然是最好的方法,还是在某个时间点后会影响性能?
    • 这个答案并不能准确回答类与接口的问题,即使它是公认的答案。 @Dakshin 的答案确实回答了二元选择,但也许应该修改问题以询问类 vs 接口 vs 枚举。
    【解决方案2】:

    有些人认为常量接口是一种反模式 (http://en.wikipedia.org/wiki/Constant_interface)。

    【讨论】:

    • 在我看来,反模式是实现一个接口以便访问命名常量。
    【解决方案3】:

    你应该在课堂上做。

    接口是类用户可以访问的可用方法、属性等的描述 - 通过实现接口,您可以保证接口中声明的成员对用户可用。

    另一方面,类是对对象的描述,或者(如果您对 OO 原则不太...)静态成员的占位符。我个人发现在某些项目中将一堆常量存储在 Settings 类中非常有用,因此我不必在整个项目中查看定义。我认为这种方法也是您所追求的。

    【讨论】:

    • 在一个类中收集你的常量,并给它一个私有构造函数以防止它被实例化。
    【解决方案4】:

    已经讨论过before:

    您不希望接口中的常量的原因是它诱使客户端类“实现”该接口(以便访问常量而不用接口名称作为前缀)。但是,您不应该这样做 - 接口实际上并不是对象功能的接口,而是根植于类的外部类型中的编译时便利。

    曾经有一段时间,“常量接口”很方便,但一直都是“错误的”,现在我们有了import static 语句,就连懒惰也不能成为使用它的借口。

    编辑:虽然我必须同意,对于您问题中提出的场景,枚举更合适。

    【讨论】:

      【解决方案5】:

      这里应该考虑使用static import(用于导入类中定义的常量),或者type-safe Enum

      来自Interface for constants

      在接口中放置常量在 Java 早期是一种流行的技术,但现在许多人认为它是一种令人讨厌的接口,因为 接口应该处理对象提供的服务,而不是它的数据强>.
      同样,类使用的常量通常是实现细节,但将它们放在接口中会将它们提升为类的公共 API。

      【讨论】:

        【解决方案6】:

        您应该将它们放在具有私有构造函数的类中。

        public class ErrorCodes {
            private ErrorCodes() {} // prevents instantiation
            public static final String ERROR_1 = "-1";
            public static final String ERROR_2 = "-2";
        

        }

        或者更好的是,使用类型安全的枚举。

        【讨论】:

          【解决方案7】:

          我建议将静态导入和常量接口结合使用。

          如果 Java 接口具有常量字段声明,请记住这些是隐式公共的、静态的和最终的(请参阅 Java 语言规范,Section 9.3。)因此,您始终可以省略这些修饰符,只留下类型,你的常量接口看起来像这样:

          public interface Constants {
              int AGE = 0x23;
              String NAME = "Andrew";
              boolean IGNORE = true;
          }
          

          当然,这应该按如下方式使用:

          import static Constants.*;
          
          public Whatever {
              public String method() {
                  return String.format("%s is %d%c", NAME, AGE, IGNORE ? '?' : '!');
              }
          }
          

          我在我的生产代码中使用这种风格没有任何问题,我觉得它带来了一个非常简洁、紧凑的常量集合,并且可以处理枚举无法处理的各种类型的常量,并且能够如果需要,可以扩展,不像枚举。

          另一种可能并非所有人都同意的方法是在父接口中嵌套接口(甚至枚举类型),从而允许您对常量进行分组。这个:

          interface MoreConstants {
              int MAGIC = 0xCAFEBABE;
              interface PROPERTIES {
                  String NAME = "name";
              }
              enum ERRORS {
                  ON_FIRE, ASLEEP, BROKEN, UNSURE;
              }
          }
          

          并像这样访问它们,假设静态导入 MoreConstants 接口:

          if (!PROPERTIES.NAME.equals(value)) {
              return ERRORS.UNSURE;
          }
          

          当然,这些接口永远不应该实现,我认为这是不好的做法。然而,确保这一点的唯一方法是严格的代码审查......

          【讨论】:

          • 一个您不会触及的问题是,一些常量将在编译时被复制到生成的字节码中,而不是被引用,从而为您留下一个 ABI,您无法在不破坏内容的情况下进行更改。
          • 由于直接字段访问,这不是“常量类”解决方案的问题吗?
          • 是的,我认为类和接口都会发生同样的事情,但我可能是错的。 +1 枚举答案
          • 指出,emum 在某些情况下更可取。这种技术最适合用于将其作为二进制 API 的一部分导出的项目。次要问题 - 我看到太多人在接口中的字段之前写“public static final”,好像它有所作为 - 了解默认修饰符是什么并使用它来编写更紧凑的代码。
          【解决方案8】:

          对常量使用接口是一种很好的做法。但是您不想实现该接口以使用常量。但使用静态导入如下。

          package com.data.job.spark;
          
          public interface Constants {
          
              /** base api url string literal. */
              public static final String BASE_API_URL = "/api/v1";
                  /** message string literal. */
              public static final String MESSAGE = "message";
          
              /** exception string literal. */
              public static final String EXCEPTION = "exception";
          }
          =================================
          import static com.data.job.spark.Constants.EXCEPTION;
          
          @Component("user360ToScyllaDbLoader")
          public class TestConstants implements Serializable{
          
          
              private static final long serialVersionUID = 1L;
          }
          

          【讨论】:

          • 这实际上回答了问题中的二元选择,即使接受的答案更符合 OP 的最佳解决方案。
          【解决方案9】:

          我个人认为应该在类中定义常量,原因与上面描述的相同。特别是因为一些开发人员通过在想要使用它们的类中实现接口来使用这些常量。当该接口包含大量常量时,您不想再查看该特定类的 javadoc,因为它充满了对该类甚至可能未使用的常量的描述。

          【讨论】:

            【解决方案10】:

            始终使用接口只定义契约和类来定义实现状态(变量和常量)和行为(方法实现)

            Define Constants in Interface or Class

            阅读这篇文章,它解释了在类和接口中定义常量的优点

            【讨论】:

              猜你喜欢
              • 2012-01-15
              • 2015-01-19
              • 1970-01-01
              • 2013-02-06
              • 2010-11-03
              • 1970-01-01
              • 2011-03-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多