Avoid Interfaces Consisting Solely of Constants
What is it?
This practice advises against using interfaces that consist only of constant definitions without any method declarations.
Why apply it?
Interfaces are intended to define APIs via function declarations and not to act as containers for constants. Using interfaces in such a manner can lead to misuse and misunderstanding of their intended purpose.
How to fix it?
Replace the interface with either an enum
or a final class with a private constructor to properly encapsulate the constants.
Examples
Example 1:
Negative
The negative example defines an interface with only constant values, which should instead be represented by an enum
.
public interface Status { /* Noncompliant */
int OPEN = 1;
int CLOSED = 2;
}
public class TaskManager {
public void setStatus(int status) {
// Implementation here
}
}
Example 2:
Negative
The negative example misuses an interface to hold a collection of color constants, which could be better represented by a final class.
public interface ColorTheme { /* Noncompliant */
int COLOR_ERROR = 0xff0000; // red
int COLOR_WARNING = 0xffff00; // yellow
int COLOR_OK = 0x00cf00; // green
}
public class ThemeManager {
public int getErrorColor() {
return ColorTheme.COLOR_ERROR;
}
}
Example 3:
Positive
The positive example uses an enum
to represent constant values, which is more appropriate when constants serve as identifiers without an essential concrete value.
public enum Status { /* Compliant */
OPEN,
CLOSED
}
public class TaskManager {
public void setStatus(Status status) {
// Implementation here
}
}
Negative
The negative example uses an interface for constants used exclusively by the WordPacker
class, leading to unnecessary exposure.
interface AuxiliaryConstants { /* Noncompliant */
int BITS_PER_WORD = 16;
int WORD_MASK = (1 << BITS_PER_WORD) - 1;
int HI_WORD_BK_MASK = ~(WORD_MASK << BITS_PER_WORD);
}
class WordPacker {
public static int getHiWord(int value) {
return (value >>> AuxiliaryConstants.BITS_PER_WORD);
}
public static int setHiWord(int value, int wordValue) {
return (value & AuxiliaryConstants.HI_WORD_BK_MASK) | (wordValue << AuxiliaryConstants.BITS_PER_WORD);
}
}
Example 4:
Positive
The positive example uses a final class with a private constructor to encapsulate constants shared across multiple classes.
public final class ColorTheme { /* Compliant */
public static final int COLOR_ERROR = 0xff0000; // red
public static final int COLOR_WARNING = 0xffff00; // yellow
public static final int COLOR_OK = 0x00cf00; // green
private ColorTheme() {} // Prevents instantiation
}
public class ThemeManager {
public int getErrorColor() {
return ColorTheme.COLOR_ERROR;
}
}
Example 5:
Positive
The positive example consolidates constants within the primary class that makes use of them, ensuring they remain implementation details.
public class WordPacker { /* Compliant */
private static final int BITS_PER_WORD = 16;
private static final int WORD_MASK = (1 << BITS_PER_WORD) - 1;
private static final int HI_WORD_BK_MASK = ~(WORD_MASK << BITS_PER_WORD);
public static int getHiWord(int value) {
return (value >>> BITS_PER_WORD);
}
public static int setHiWord(int value, int wordValue) {
return (value & HI_WORD_BK_MASK) | (wordValue << BITS_PER_WORD);
}
}