Should a collection of constants be placed in a cl

2019-01-17 03:43发布

问题:

If I have a collection of static constants that I want to declare centrally so that they can be shared among various projects should they be put in a class or interface (Java).

In the past I have seen them mostly put in a class but I started thinking that since the class will not and should not be instantiated maybe they would be better in an interface, but then again the interface should not be implemented by any classes, e.g.

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

or

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

回答1:

If they have strong connections, then I'd put them in an enum:

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

The advantage is that you can have type safety in your code and that you can easily add id-based lookup (either by building a HashMap<String,Error> in the constructor or by simply looping over values()).



回答2:

Some people consider constant-interface an anti-pattern (http://en.wikipedia.org/wiki/Constant_interface) .



回答3:

You should do it in a class.

An Interface is a description of available methods, properties etc that class users can access - by implementing an interface, you guarantee that the members declared in the interface are available to the user.

A class, on the other hand, is a description of an object or (if you're not too hard on the OO principles...) a placeholder for static members. I personally find it very useful in some projects to store away a bunch of constants in a Settings class, so I don't have to look around in the entire project for the definitions. I think this approach is what you're after, too.



回答4:

This has been discussed before:

The reason why you don't want the constants in an interface is that it entices client classes to "implement" that interface (in order to access the constants without prefixing them with the interface name). You shouldn't, though - the interface isn't actually an interface to the object's capabilities, but a compile-time convenience ingrained in the class' external type.

There was a time when the "constant interface" was very convenient, but it has always been "wrong" and not even laziness is an excuse to use it now that we have import static statements.

Edit: Although I must agree that for the scenario presented in your question, enums are more appropriate.



回答5:

The use of static import should be considered here (for importing constants defined in a class), or type-safe Enum.

From Interface for constants

Placing constants in an interface was a popular technique in the early days of Java, but now many consider it a distasteful use of interfaces, since interfaces should deal with the services provided by an object, not its data.
As well, the constants used by a class are typically an implementation detail, but placing them in an interface promotes them to the public API of the class.



回答6:

You should put them on the class with a private constructor.

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

}

Or better yet, use a typesafe enum.



回答7:

I recommend a combination of static imports and interfaces for constants.

If a Java interface has constant field declarations, bear in mind that these are implicitly public, static and final (see the Java Language Specification, Section 9.3.) You can therefore always omit these modifiers, leaving only the type, and your constant interface would look like this:

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

This should, of course, only ever be used as follows:

import static Constants.*;

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

I have no issues using this style in my production code, and feel it leads to a very neat, compact constants collection, and can cope with various types of constant, which an enum couldn't, as well as being able to be extended if requred, unlike an enum.

Another possibility, which not everyone will approve of, is to nest interfaces (or even enum types) in your parent interface, allowing you to group your constants. This :

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

and access them like this, assuming a static import of the MoreConstants interface:

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

Of course, these interfaces should never be implemented, which I would consider bad practice. The only way to ensure this, however, is strict code reviews...



回答8:

I personally feel that the constants should be defined in a class for the same reasons as described here above. Especially because some developers use these constants by implementing the interface in the class that wants to use them. When that interface contains a lot of constants you do not want to look at the javadoc of that specific class anymore because it is littered with descriptions of constants that are probably not even used by that class.



回答9:

Using Interface for constants is a good practice. But you do not want to implement that interface to use the constants. But use static import as below.

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