118

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

}
Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
kitsune
  • 11,516
  • 13
  • 57
  • 78
  • 15
    Note: `static final` is not necessary, it is redundant for an interface. – ThomasW Sep 07 '16 at 09:57
  • Also note that `platformNames` may be `public`, `static` and `final`, but it is definitely not a constant. The only constant array is one with zero length. – Vlasec Dec 16 '16 at 12:49
  • @ThomasW I know this is a few years old, but I needed to point out an error in your comment. `static final` is not necessarily redundant. A class or interface field with only the `final` keyword would create separate instances of that field as you create objects of the class or interface. Using `static final` would make each object share a memory location for that field. In other words, if a class MyClass had a field `final String str = "Hello";`, for N instances of MyClass, there would be N instances of the field str in memory. Adding the `static` keyword would result in only 1 instance. – Sintrias Dec 19 '19 at 13:56
  • 1
    @Sintrias that's the case for classes but not interfaces. In an interface any field declared is implicitly static. – awgtek Jan 07 '22 at 22:05
  • @awgtek Thanks for the correction. I misunderstood ThomasW's comment and didn't know that's how interfaces work in Java. – Sintrias Jan 18 '22 at 00:28

8 Answers8

164

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
  • 53,737
  • 19
  • 129
  • 165
  • 1
    It's worth noting that in some cases you have no choice, for example JavaME. – izb Nov 26 '08 at 14:23
  • 8
    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 Nov 26 '08 at 14:49
  • 12
    BTW: You can use an enum with no instances as a class which cannot be instantiated. ;) – Peter Lawrey Dec 30 '10 at 20:59
  • @Dan Dyer the problem with enums is, it forces you to declare a new type which may not fit. For instance in my JavaFX2 application, my UI has 2 constants depending on which button was clicked 'Load', 'Submit'. It gives it to a background Service as a memento so that when the handler is called when the Service returns, the handler retrieves the memento. I have defined the memento as an int in the Service because I don't want a UI enum ButtonClicked to 'pollute' the Service. So Load and Submit are int constants defined in the UI controller interface. If you have a better way, please let me know! – likejudo Feb 26 '13 at 16:33
  • 6
    But why implementing those interfaces in the first place? Why not using them just as constants repository? If I need some kind of constants shared globally I don't see "cleaner" ways of doing it. – shadox Aug 20 '16 at 11:52
  • @shadox If you're not going to implement the interface you might as well just use a class or an enum. – Dan Dyer Aug 22 '16 at 11:07
  • 3
    @DanDyer yeah, but an interface makes some declarations implicit. Like *public static final* is just a default. Why bother with a class? Enum - well, it depends. An enum should define a collection of possible values for an entity and not a collection of values for different entities. – shadox Aug 25 '16 at 00:41
  • 4
    Personally I feel Josh is hitting the ball wrong. If you do not want your constants to leak through - regardless in what type of object you put them - you need to make sure they are NOT part of the exported code. Interface or class, both can be exported. So the right question to ask is not: in what type of object do I put them in but how do I organize this object. And if the constants are used in exported code, you still to make sure they are available once exported. So the "bad practice" claim is in my humble opinion invalid. – Lawrence May 14 '18 at 12:46
103

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
  • 22,200
  • 14
  • 65
  • 81
  • 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 Nov 26 '08 at 13:06
  • 13
    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 Nov 26 '08 at 13:11
  • "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 Nov 26 '08 at 13:17
  • 4
    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 Nov 26 '08 at 13:23
  • 3
    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 Nov 26 '08 at 13:55
  • Good answer. If easily-generated boilerplate annoys someone so much that they would purposefully write fake "interfaces" that are easily abused, they should probably use a language other than Java. Java has tons of easily-generated boilerplate, everywhere. – Matthew Read May 30 '23 at 15:24
9

I do not pretend the right to be right, but lets see this small example:

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

ToyotaCar doesnt know anything about FordCar, and FordCar doesnt know about ToyotaCar. principle CarConstants should be changed, but...

Constants should not be changed, because the wheel is round and egine is mechanical, but... In the future Toyota's research engineers invented electronic engine and flat wheels! Lets see our new interface

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

and now we can change our abstraction:

public interface ToyotaCar extends CarConstants

to

public interface ToyotaCar extends InnovativeCarConstants 

And now if we ever need to change the core value if the ENGINE or WHEEL we can change the ToyotaCar Interface on abstraction level, dont touching implementations

Its NOT SAFE, I know, but I still want to know that do you think about this

pleerock
  • 18,322
  • 16
  • 103
  • 128
  • I want to know you idea now in 2019. For me interface fields is meant to be shared among some objects. – NeoZoom.lua Apr 27 '19 at 06:50
  • I wrote an answer related to your idea: https://stackoverflow.com/a/55877115/5290519 – NeoZoom.lua Apr 27 '19 at 06:51
  • this is a good example of how PMD's rules are very limited. Trying to get better code through bureaucracy remains a futile attempt. – bebbo May 07 '19 at 09:17
8

There is a lot of hate for this pattern in Java. However, an interface of static constants does sometimes have value. You need to basically fulfill the following conditions:

  1. The concepts are part of the public interface of several classes.

  2. Their values might change in future releases.

  3. Its critical that all implementations use the same values.

For example, suppose that you are writing an extension to a hypothetical query language. In this extension you are going to expand the language syntax with some new operations, which are supported by an index. E.g. You are going to have a R-Tree supporting geospatial queries.

So you write a public interface with the static constant:

public interface SyntaxExtensions {
     // query type
     String NEAR_TO_QUERY = "nearTo";

     // params for query
     String POINT = "coordinate";
     String DISTANCE_KM = "distanceInKm";
}

Now later, a new developer thinks he needs to build a better index, so he comes and builds an R* implementation. By implementing this interface in his new tree he guarantees that the different indexes will have identical syntax in the query language. Moreover, if you later decided that "nearTo" was a confusing name, you could change it to "withinDistanceInKm", and know that the new syntax would be respected by all your index implementations.

PS: The inspiration for this example is drawn from the Neo4j spatial code.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
phil_20686
  • 4,000
  • 21
  • 38
6

Given the advantage of hindsight, we can see that Java is broken in many ways. One major failing of Java is the restriction of interfaces to abstract methods and static final fields. Newer, more sophisticated OO languages like Scala subsume interfaces by traits which can (and typically do) include concrete methods, which may have arity zero (constants!). For an exposition on traits as units of composable behavior, see http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf. For a short description of how traits in Scala compare with interfaces in Java, see http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5. In the context of teaching OO design, simplistic rules like asserting that interfaces should never include static fields are silly. Many traits naturally include constants and these constants are appropriately part of the public "interface" supported by the trait. In writing Java code, there is no clean, elegant way to represent traits, but using static final fields within interfaces is often part of a good workaround.

Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
1

According to JVM specification, fields and methods in a Interface can have only Public, Static, Final and Abstract. Ref from Inside Java VM

By default, all the methods in interface is abstract even tough you didn't mention it explicitly.

Interfaces are meant to give only specification. It can not contain any implementations. So To avoid implementing classes to change the specification, it is made final. Since Interface cannot be instantiated, they are made static to access the field using interface name.

om39a
  • 1,406
  • 4
  • 20
  • 39
0

I do not have enough reputation to give a comment to Pleerock, therefor do I have to create an answer. I am sorry for that, but he put some good effort in it and I would like to answer him.

Pleerock, you created the perfect example to show why those constants should be independent from interfaces and independent from inheritance. For the client of the application is it not important that there is a technical difference between those implementation of cars. They are the same for the client, just cars. So, the client wants to look at them from that perspective, which is an interface like I_Somecar. Throughout the application will the client use only one perspective and not different ones for each different car brand.

If a client wants to compare cars prior to buying he can have a method like this:

public List<Decision> compareCars(List<I_Somecar> pCars);

An interface is a contract about behaviour and shows different objects from one perspective. The way you design it, will every car brand have its own line of inheritance. Although it is in reality quite correct, because cars can be that different that it can be like comparing completely different type of objects, in the end there is choice between different cars. And that is the perspective of the interface all brands have to share. The choice of constants should not make this impossible. Please, consider the answer of Zarkonnen.

Loek Bergman
  • 2,192
  • 20
  • 18
-1

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
  • 11,819
  • 6
  • 44
  • 61
  • 2
    Prior to Java 5, you could use the type-safe enum pattern (see http://java.sun.com/developer/Books/shiftintojava/page1.html). – Dan Dyer Nov 26 '08 at 13:09