Item 19: Use interfaces only to define types

In the book Effective Java 2nd edition, the item 19 stipulates to "use interfaces only to define types" and not to use them to define constants.

Here is an example of that antipattern:

  // Constant interface is an antipattern, don't do that
  public interface MyConstants {
      static final double PI = 3.14;
  }

The argument is that an interface is meant to be implemented and used to define a type. Using an interface to define constants can be misleading as well as encouraging developer to implement the interface for the sole reason to access the constants.

The recommendation is to use an utility class and static import.

  // Constant class: use it with static import
  //
  // import static my.package.MyConstants.*;
  public class MyConstants {
      private MyConstants () {} // prevents instantiation
      public static final double PI = 3.14;
  }

However I was rather sceptical because nothing here prevents the class to be instanciated as well. Also a previous item (item 3) in the book warns that, even with a private constructor, a class could be instantiated by reflection.

Thus I think it would be better to make the class final and throw an exception.

  public final class MyConstants {
      public static final double PI = 3.14;

      private MyConstants() { // prevents instantiation 
          throw new AssertionError();
      }
  }

In summary, to create an utility class for constants:

  • make the class final to prevent its extension
  • make the constructor private and throw an exception to prevent instantiation
  • use static import to access the constants without having to specify the name of the constant class (optional).

It may look like nitpicking (it may very well be, I doubt I would do more than raise an eyebrow if I saw an interface to provide constants during a code review although I would probably not accept a class implementing an interface to access the constants) but I was a bit confused by the argument being made.