“This is the 10th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
Hello, I’m looking at the mountains.
In real development, it is often necessary to define a file to store constants that are designed to be static public constants (using the public static final modifier). There are two options:
- Define constants in interfaces, such as JDK 1.1
java.io.ObjectStreamConstans
Interface; - Define constants in classes, such as in JDK 1.7
java.nio.charset.StandardCharsets
;
Both approaches do the trick: store constants without instantiation. The following is a case to discuss the advantages and disadvantages of the two methods.
Constant interface
All variables defined in the interface must be constants. By default, the public static final modifier is used. In other words, when writing code, you write the following directly:
public interface ObjectStreamConstants {
short STREAM_MAGIC = (short)0xaced;
short STREAM_VERSION = 5;
}
Copy the code
But in the class it must be written like this:
public final class ObjectStreamConstants {
public static final short STREAM_MAGIC = (short)0xaced;
public static final short STREAM_VERSION = 5;
}
Copy the code
From the intuitive feeling, the interface is much easier to write. Second problem: Because there are more characters written in the class than in the interface, the compiled file is also larger than the interface file. Third problem: Interfaces don’t have the extra special features that classes provide during JVM loading (such as overloading, dynamic binding of methods, etc.), so they load faster than classes. At this point, there seems to be no reason not to use interfaces to define constants. BUT, this is a serious anti-pattern behavior. To quote from Effective Java:
The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class’s exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface.
The translation is:
Constant interface mode is a bad use of an interface. The class uses some constants internally, which is purely an implementation detail. Implementing the constant interface results in leaking such implementation details into the class’s export API. The class implements a constant interface, which is of little value to the user of the class. In fact, it only makes them more confused. Worse, it represents a promise that if, in a future release, the class is modified so that it no longer needs to use these constants, it will still have to implement the interface to ensure compatibility. If a non-final class implements a constant interface, the namespace of all its subclasses will also be “polluted” by constants in the interface.
This makes it very clear:
-
An interface cannot be prevented from being implemented or inherited. That is, the definition of a constant can be overridden in a subinterface or implementation, so references to a constant through a parent or child interface (or implementation) may be inconsistent.
-
Similarly, as a result of being implemented or inherited, a constant can be referenced by a large number of interfaces, classes, or instances in the inheritance tree, resulting in constant defined in the interface contaminating the namespace.
-
The implication of an interface is that it is implemented, represents a type, and its public members are exposed APIS, but constants defined in the interface are not apis.
To sum up: using interfaces to define constants is a bad behavior. The JDK constants defined in the interface (such as Java. IO. ObjectStreamConstans) should be a cautionary tale.
The class interface
Since using the interface’s first constant is not an option, you can only define constants through classes. Although classes cannot be multiinherited in JAVA, subclasses can also “pollute” the namespace in which their parent class defines constants. In order for constants to be immutable, we need to make the constant class final, and then, to be more thorough, we need to define a private constructor. Like Java nio. Charset. StandardCharsets:
public final class StandardCharsets {
private StandardCharsets(a) {
throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!");
}
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
public static final Charset UTF_8 = Charset.forName("UTF-8");
}
Copy the code
In Java nio. Charset. StandardCharsets, in order to prevent the instantiation of the various forms, and even throw an error in the constructor, and do a thorough enough.
Enumerated type
BUT, there is another case where a constant defines a gender: male, female, using the above class constant, which needs to be written:
public final class Gender {
private Gender(a) {
throw new AssertionError("No x.y.z.Gender instances for you!");
}
public static final int MALE = 1;
public static final int FEMALE = 0;
}
Copy the code
Because the gender type is actually int, if you write m. setgender (3), it’s not wrong. Is it four, five, six, seven? Then this constant definition loses its value. For such categorizable constants, the best way to define constants is through enumerations:
public enum Gender {
MALE,
FEMALE
}
Copy the code
According to the edited bytecode, Gender is actually:
public final class Gender extends java.lang.Enum {
public static final Gender MALE;
public static final Gender FEMALE;
}
Copy the code
Methods that accept type Gender then have to pass MALE or FEMALE, no more options, and that’s what enumeration is all about. In subsequent JDK, also appeared like Java. Nio. File. Enumeration definition of StandardOpenOption etc. Enumeration definitions are not limited to this, but there are many other complex definitions that can be applied to a variety of situations, which we’ll discuss later.
conclusion
Instead of using interface constants, define constants ina class, preferably a final class, and define private constructors. If constants can be categorized, use enumerations: enumeration > Class > interface.
This article was picked up by Java Advancements.
Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow. Welcome to follow the public account “Mountain Hut”, discover a different world.