views:

2404

answers:

3

I'm looking at some open source Java projects to get into Java and notice a lot of them have some sort of 'constants' interface.

For instance, processing.org has an interface called PConstants.java, and most other core classes implement this interface. The interface is riddled with static members. Is there a reason for this approach, or is this considered bad practice? Why not use enums where it makes sense, or a static class?

I find it strange to use an interface to allow for some sort of pseudo 'global variables'.

  public interface PConstants {

  // LOTS OF static fields...

  static public final int SHINE = 31;

  // emissive (by default kept black)
  static public final int ER = 32;
  static public final int EG = 33;
  static public final int EB = 34;

  // has this vertex been lit yet
  static public final int BEEN_LIT = 35;

  static public final int VERTEX_FIELD_COUNT = 36;


  // renderers known to processing.core

  static final String P2D    = "processing.core.PGraphics2D";
  static final String P3D    = "processing.core.PGraphics3D";
  static final String JAVA2D = "processing.core.PGraphicsJava2D";
  static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
  static final String PDF    = "processing.pdf.PGraphicsPDF";
  static final String DXF    = "processing.dxf.RawDXF";


  // platform IDs for PApplet.platform

  static final int OTHER   = 0;
  static final int WINDOWS = 1;
  static final int MACOSX  = 2;
  static final int LINUX   = 3;

  static final String[] platformNames = {
    "other", "windows", "macosx", "linux"
  };

  // and on and on

  }
A: 

This came from a time before Java 1.5 exists and bring enums to us. Prior to that, there was no good way to define a set of constants or constrained values.

This is still used, most of the time either for backward compatibility or due to the amount of refactoring needed to get rid off, in a lot of project.

gizmo
Prior to Java 5, you could use the type-safe enum pattern (see http://java.sun.com/developer/Books/shiftintojava/page1.html).
Dan Dyer
+7  A: 

Instead of implementing a "constants interface", in Java 1.5+, you can use static imports to import the constants/static methods from another class/interface:

import static com.kittens.kittenpolisher.KittenConstants.*;

This avoids the ugliness of making your classes implement interfaces that have no functionality.

As for the practice of having a class just to store constants, I think it's sometimes necessary. There are certain constants that just don't have a natural place in a class, so it's better to have them in a "neutral" place.

But instead of using an interface, use a final class with a private constructor. (Making it impossible to instantiate or subclass the class, sending a strong message that it doesn't contain non-static functionality/data.)

Eg:

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}
Zarkonnen
So, you're explaining that, because of the static import, we should use classes instead of intefaces to re-do the same error as before?! That's silly!
gizmo
No, that's not what I'm saying at all. I'm saying two independent things. 1: use static imports instead of abusing inheritance. 2: If you must have a constants repository, make it a final class instead of an interface.
Zarkonnen
"Constant Interfaces" where not designed to be part of any inheritance, never. So the static import is just for syntactic sugar, and inherit from such interface is an horrible error. I know Sun did this, but they also made tons of other basic errors, that's not an excuse to mimic them.
gizmo
One of the problems with the code posted for the question is that interface implementation is used just to get easier access to constants. When I see something implement FooInterface, I expect that to impact its functionality, and the above violates this. Static imports fix that problem.
Zarkonnen
gizmo - I'm not a fan of static imports, but what he's doing there is avoiding having to use the class name, i.e. ConstClass.SOME_CONST. Doing a static import doesn't add those members to the class you Z. doesn't say to inherit from the interface, he says the opposite in fact.
mtruesdell
+1 for a final class with a private constructor to hold constants
matt b
+14  A: 

It's generally considered bad practice. The problem is that the constants are part of the public "interface" (for want of a better word) of the implementing class. This means that the implementing class is publishing all of these values to external classes even when they are only required internally. The constants proliferate throughout the code. An example is the SwingConstants interface in Swing, which is implemented by dozens of classes that all "re-export" all of its constants (even the ones that they don't use) as their own.

But don't just take my word for it, Josh Bloch also says it's bad:

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.

An enum may be a better approach. Or you could simply put the constants as public static fields in a class that cannot be instantiated. This allows another class to access them without polluting its own API.

Dan Dyer
It's worth noting that in some cases you have no choice, for example JavaME.
izb
Enums are a red herring here - or at least a separate question. Enums should of course be used, but they should also be hidden if they are not needed by implementors.
DJClayworth