7

The question of where to define constants in Java has appeared numerous times in forums, yet I am struggling to settle on a solution I feel comfortable with.

To make it simple, suppose I have two classes: WriteMyData and ReadMyData. None is a subclass of the other. Both classes share two constants that are vital for their operation: String DELIMITER and int LENGTH. In the future I might want to change the value of these constants so they should be defined somewhere appropriate.

The consensus seems to often favour the enum type. However, there is nothing to enumerate in my case, so I end up with only one item in my enum which I call 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; }
}

Thus, in both my classes I access the constants through DataSettings.DEFAULT.getDelimiter() and DataSettings.DEFAULT.getLength().

Is this really good OO style? Is the use of enum perhaps overkill? If I do not use enum, what should I do instead? Creating an interface for constants seems to be frowned upon, and there seems to be no natural superclass for my classes to derive from. Is it a beginners mistake to have only one default item in an enum?

DustByte
  • 651
  • 6
  • 16
  • If there's nothing to enumerate - ie there's only ONE data setting - just make it a static class with delimiter and length as public static final variables – mixmastered Jan 07 '16 at 11:56
  • Does this both variable points to same value? do you want constant varible declare in one place? – bNd Jan 07 '16 at 11:57
  • Ideally, there should be one extra class which has all the variables as `public static final`. – Anindya Dutta Jan 07 '16 at 11:57
  • The names of the classes suggest that they could be better modeled as one class with `read` and `write` methods, which would solve your dilemma in this particular case. – fishinear Jan 07 '16 at 12:08
  • @fishinear I did play with this idea, but for the purpose of this question, ignore the actual classes and think of them as unrelated, only that they do share a couple of constants. – DustByte Jan 07 '16 at 12:12
  • @DustByte so what did you end up doing? – rinde Jan 20 '16 at 20:24
  • @rinde - I ended up doing a merge of the two classes `WriteMyData` and `ReadMyData`, hence I could define the two constants as static in the merged class. Remodelling seems to be a solution to avoid dilemmas like the one above. :) – DustByte Jan 28 '16 at 15:45
  • Points for remodelling! I guess you could make them private then? Even cleaner IMO. – rinde Jan 28 '16 at 15:48

8 Answers8

3

Just create something like Constants.java class where you will put all the constants.

For example:

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

And use them where you want by:

Constants.DELIMITER
Constants.LENGTH
antoniodvr
  • 1,259
  • 1
  • 14
  • 15
  • Explain to OP why class not interface? – bNd Jan 07 '16 at 12:04
  • 2
    I gather from reading in forums that having a class merely for constants is bad practice as it tends to be littered over time with unrelated constants appearing far from where they are applicable. – DustByte Jan 07 '16 at 12:06
  • 1
    @bmt Simply because interfaces have specified purpose and certainly storing constants is not one of them. Interfaces are for specyfing API, not for storing data. In every use case different from what interfaces are for, class should be used. FIrstly I would ask why to use an interface here. And if the reason to use them is because its variables are implicitly `public static final` and we don't have to do it by hand it's not a good reason. To comply to OOP rules class should be used. – ctomek Jan 09 '16 at 13:40
2

If only those two constans and not going to have more than that, You can have a Interface like

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

And if you need to initilize through property

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 
  }
}
vels4j
  • 11,208
  • 5
  • 38
  • 63
1

Using an enum when there is nothing to enumerate is indeed bad practice.

The other mentioned alternative, using an interface is also a poor choice. Effective Java, Item 19 describes it best:

Item19: Use interfaces only to define types

When a class implements an interface, the interface serves as a type that can be used to refer to instances of the class. That a class implements an interface should therefore say something about what a client can do with instances of the class. It is inappropriate to define an interface for any other purpose.

One kind of interface that fails this test is the so-called constant interface. Such an interface contains no methods; it consists solely of static final fields, each exporting a constant. Classes using these constants implement the interface to avoid the need to qualify constant names with a class name.

The correct implementation is to define a non-instantiable utility class:

public class Constants {
    private Constants(){} // Private constructor prevents instantiation AND subclassing

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

For convenience you can statically import the constants in your code:

import static com.example.Constants.*;

public class Test {
    public static void main(String[] args){
        System.out.println(DELIMITER); // prints "-"
    }
}
Community
  • 1
  • 1
rinde
  • 1,181
  • 1
  • 8
  • 20
0

An enumeration with just 1 value might not make much sense, although it might be useful if you plan on extending whatever values it could contain.

An alternative to what you are saying would be to do as follows:

  1. Have a public class which exposes project-wide constants. You could make this class load it's values from some configuration file when your application is starting up so that you can control the values of these constants.

  2. Have a separate set of methods suffixed with WithDefaultValues for your reading and writing methods. These methods will, in turn, call your other methods and pass in the default parameters.

As a side note, it might make sense to simply overload the methods you already have so that you have an implementation which defaults to these constants. If that is the case, be sure to document this in your method's signature.

npinti
  • 51,780
  • 5
  • 72
  • 96
0

IMO, Enum is overkill in this case. Enums are made for enumerations.

For global constants, you can just create a public static final class attribute in a java class or java interface, although the latest one is not the usual approach (see Interfaces with static fields in java for sharing 'constants')

Community
  • 1
  • 1
Leo
  • 751
  • 4
  • 29
0

I would propose another solution that doesn't use any constants in WriteMyData and ReadMyData.

Do pass delimiter and length as constructor parameters. This way you will be able to unit test these classes with parameters that may make testing more easy.

When it is important that an instance of each uses the same values for delimiter and length, then both should be instantiated at the same location and made available for clients to use. The location where the instances are created is a proper place to have constants for the values of delimiter and length.

SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
0

You can create an interface.
By default constants in interface are static and final.
And you can user those variable by Referencing interface.

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

Use as:

AnyNameInterface.AnyVar
AnyNameInterface.AnyVar_2
rptwsthi
  • 10,094
  • 10
  • 68
  • 109
0

I would like to add that even when there are something to enumerate, enums can't always be used as a container for constants, even though they are nice.

For instance, javax.ws.rs.Path defines a single annotation type element of type String. However, this won't compile:

@Path(MyEnum.BASE_PATH) nor will @Path(MyEnum.BASE_PATH.name())

Also, in those cases where it is not possible to define the constants on the class itself, or on some common superclass, the discussion often seem to be between defining the constants in a class vs in an interface. A third option could be to define them in an annotation.

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

The annotation approach steers clear of the "constant interface" pitfall. This pitfall is, in my understanding, not that the interface as a constant container in itself is a bad idea, - the problem is when callers decide to implement the interface, rather than accessing the constants through eg. a static import.

Hervian
  • 1,066
  • 1
  • 12
  • 20