DustByte
DustByte

Reputation: 813

Where to define constants in Java that are used in a couple of unrelated classes

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?

Upvotes: 7

Views: 6645

Answers (8)

Hervian
Hervian

Reputation: 1076

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.

Upvotes: 0

rinde
rinde

Reputation: 1263

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 "-"
    }
}

Upvotes: 1

user5757551
user5757551

Reputation: 1

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

Upvotes: 0

SpaceTrucker
SpaceTrucker

Reputation: 13556

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.

Upvotes: 0

vels4j
vels4j

Reputation: 11308

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 
  }
}

Upvotes: 2

Leonardo Kenji Shikida
Leonardo Kenji Shikida

Reputation: 761

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')

Upvotes: 0

antoniodvr
antoniodvr

Reputation: 1259

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

Upvotes: 3

npinti
npinti

Reputation: 52205

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.

Upvotes: 0

Related Questions