【问题标题】:Where to define constants in Java that are used in a couple of unrelated classes在Java中定义几个不相关的类中使用的常量的位置
【发布时间】:2016-04-11 19:19:03
【问题描述】:

在 Java 中在哪里定义常量的问题已经在论坛中多次出现,但我正在努力寻找一个我觉得舒服的解决方案。

为简单起见,假设我有两个类:WriteMyDataReadMyData。 None 是另一个的子类。这两个类共享两个对其操作至关重要的常量:String DELIMITERint LENGTH。将来我可能想更改这些常量的值,以便在适当的地方定义它们。

共识似乎经常支持enum 类型。但是,就我而言,没有什么可以枚举,所以我的enum 中只有一项我称之为DEFAULT

public enum DataSettings {
    DEFAULT(",", 32);

    private final String delimiter;
    private final int length;

    DataSettings(String delmiter, int length) {
        this.delimiter = delimiter;
        this.length = length;
    }

    public String getDelimiter() { return delimiter; }
    public int getLength() { return length; }
}

因此,在我的两个类中,我都通过DataSettings.DEFAULT.getDelimiter()DataSettings.DEFAULT.getLength() 访问常量。

这真的是很好的OO风格吗?使用enum 是不是有点矫枉过正?如果我不使用enum,我应该怎么做?为常量创建接口似乎不受欢迎,而且似乎没有自然的超类可供我的类派生。 enum 中只有一个 default 项是初学者的错误吗?

【问题讨论】:

  • 如果没有可枚举的内容——即只有一个数据设置——只需将其设为一个静态类,并将其定界符和长度作为公共静态最终变量
  • 这两个变量是否指向相同的值?你想在一个地方声明常量吗?
  • 理想情况下,应该有一个额外的类,它的所有变量都为public static final
  • 类的名称表明它们可以更好地建模为一个具有readwrite 方法的类,这将解决您在这种特殊情况下的困境。
  • @fishinear 我确实玩过这个想法,但出于这个问题的目的,忽略实际的类并将它们视为不相关的,只是它们共享几个常量。

标签: java enums constants


【解决方案1】:

如果只有这两个常量并且不会有更多,你可以有一个类似的接口

interface DataSetting
{
   String DELIMITER = ",";
   int LENGTH = 32;
}

如果你需要通过属性初始化

public class DataSetting {

   public static String DELIMITER = ",";
   public static int LENGTH = 32;

  static {
    DELIMITER = System.getProperty("delimiter");
    LENGTH = Integer.parseInt(System.getProperty("length"));
     // or from config 
  }
}

【讨论】:

  • @Eng.Fouad 复制粘贴错误 :-)
  • 你可以删除它们,因为它们是多余的:)
  • 很多人似乎避免为此使用接口,这让我很害怕,但显然它被称为常量接口:en.wikipedia.org/wiki/Constant_interface
  • 静态成员是接口的最佳/坏习惯吗?
  • @DustByte 然后你可以选择选项 2
【解决方案2】:

只有 1 个值的枚举可能没有多大意义,但如果您计划扩展它可能包含的任何值,它可能会很有用。

您所说的另一种方法是执行以下操作:

  1. 有一个公开项目范围常量的公共类。你可以让这个类在你的应用程序启动时从某个配置文件中加载它的值,这样你就可以控制这些常量的值。

  2. 为您的读写方法提供一组单独的以WithDefaultValues 为后缀的方法。这些方法将依次调用您的其他方法并传入默认参数。

作为旁注,简单地重载你已经拥有的方法可能是有意义的,这样你就有一个默认为这些常量的实现。如果是这种情况,请务必在您的方法签名中记录这一点。

【讨论】:

    【解决方案3】:

    只需创建类似Constants.java 的类,您将在其中放置所有常量。

    例如:

    public class Constants {
        public static final String DELIMITER = "-";
        public static final int LENGTH = 1;
    }
    

    然后在你想要的地方使用它们:

    Constants.DELIMITER
    Constants.LENGTH
    

    【讨论】:

    • 向 OP 解释为什么类没有接口?
    • 我从论坛上的阅读中得知,只为常量设置一个类是不好的做法,因为随着时间的推移,它往往会乱七八糟,不相关的常量出现在远离它们适用的地方。
    • @bmt 仅仅因为接口有指定的用途,当然存储常量不是其中之一。接口用于指定 API,而不是用于存储数据。在每个与接口不同的用例中,都应该使用类。首先我会问为什么在这里使用接口。如果使用它们的原因是因为它的变量是隐含的public static final 而我们不必手动操作,这不是一个好的理由。为了遵守 OOP 规则,应该使用类。
    【解决方案4】:

    IMO,在这种情况下,枚举是矫枉过正的。 Enums 用于枚举。

    对于全局常量,您可以在 java 类或 java 接口中创建一个 public static final 类属性,尽管最新的方法不是通常的方法(参见Interfaces with static fields in java for sharing 'constants'

    【讨论】:

      【解决方案5】:

      我会提出另一种解决方案,它不使用WriteMyDataReadMyData 中的任何常量。

      将分隔符和长度作为构造函数参数传递。这样您就可以使用参数对这些类进行单元测试,从而使测试更容易。

      如果每个实例使用相同的分隔符和长度值很重要,则应在同一位置实例化两者并供客户端使用。创建实例的位置是为 delimiter 和 length 值设置常量的合适位置。

      【讨论】:

        【解决方案6】:

        您可以创建一个界面。
        默认情况下,接口中的常量是静态的和最终的。
        您可以通过引用接口来使用这些变量。

        public interface AnyNameInterface {
           String AnyVar ="DEMO";
           Double AnyVar_2 = 123.00;
        }
        

        用作:

        AnyNameInterface.AnyVar
        AnyNameInterface.AnyVar_2
        

        【讨论】:

          【解决方案7】:

          在没有可列举的情况下使用enum 确实是不好的做法。

          另一个提到的选择,使用interface 也是一个糟糕的选择。 Effective Java,第 19 条描述得最好:

          第 19 条:仅使用接口来定义类型

          当一个类实现一个接口时,该接口作为一个type,可以用来引用该类的实例。因此,一个类实现了一个接口应该说明客户端可以对类的实例做什么。为任何其他目的定义接口是不合适的。

          未能通过此测试的一种接口是所谓的常量接口。这样的接口不包含任何方法;它仅由静态最终字段组成,每个字段都导出一个常量。使用这些常量的类实现该接口以避免使用类名来限定常量名。

          正确的实现是定义一个不可实例化的工具类:

          public class Constants {
              private Constants(){} // Private constructor prevents instantiation AND subclassing
          
              public static final String DELIMITER = "-";
              public static final int LENGTH = 1;
          }
          

          为方便起见,您可以在代码中静态导入常量:

          import static com.example.Constants.*;
          
          public class Test {
              public static void main(String[] args){
                  System.out.println(DELIMITER); // prints "-"
              }
          }
          

          【讨论】:

            【解决方案8】:

            我想补充一点,即使有要枚举的东西,枚举也不能总是用作常量的容器,即使它们很好。

            例如,javax.ws.rs.Path 定义了字符串类型的单个注释类型元素。但是,这不会编译:

            @Path(MyEnum.BASE_PATH) 也不会 @Path(MyEnum.BASE_PATH.name())

            此外,在那些不可能在类本身或某些常见超类上定义常量的情况下,讨论似乎通常是在类中定义常量与在类中定义常量之间进行讨论。一个界面。第三种选择可能是在注释中定义它们。

            public @interface MyAnnotation {
              String QUESTIONS = "/questions";
              String ANSWERS = "/answers"; }
            

            注释方法避开了“常量接口”的陷阱。在我的理解中,这个陷阱并不是接口本身作为一个常量容器是一个坏主意,问题是当调用者决定实现接口时,而不是通过例如访问常量。静态导入。

            【讨论】:

              猜你喜欢
              • 2017-10-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多