How do I define constants that must be overridden inside an interface?

1.9k views Asked by At

To create kits that players can choose, I have made an interface:

public interface Kit {}

And I have implemented it for each kit:

public class Ninja implements Kit {}

Now, I want to set some constants related to the class, not the instance. I want these to be static across all implementations of the interface, and I want each implementation to override them.

Try #1:

public interface Kit {
    String DISPLAY_NAME;
    // The blank final field DISPLAY_NAME may not have been initialized
}

Try #2:

public interface Kit {  
    static String getDisplayName();
    // Illegal modifier for the interface method getDisplayName; only public & abstract are permitted
}
3

There are 3 answers

0
Emz On

An interface can not hold data the way a class can hold a field. If you do not want your Kit to be instantiated, you most likely want an abstract class. See them as an interface that can have some implementation and fields.

Note, please read for further clarfication: Read More

So what you want in this to have an abstract class in the background, not an interface. Now how does that look?

public abstract class Kit {
    protected final String name = "Foo";

    public String getName () {
        return name;
    }
}

Here we have our Kit, every class implementing Kit will have access to the name field. I might recommend putting it in caps if it is supposed to be a constant. It might be best with a static property as well. More of that can be read here.

To illustrate I've made two classes inherit from our abstract class Kit. Ninja and Test.

public class Ninja extends Kit {
}

This class purpose is just to check if name really has the value of Foo or not.

Then we need our actual test class as well.

public class Test extends Kit {
    public static void main (String[] args) {
        Test ninja = new Test ();
        System.out.println(ninja.getName());    // foo
        Ninja ninja2 = new Ninja ();
        System.out.println(ninja2.getName());   // foo
    } 
}

They are both of different types, Test resp. Ninja but they both have the value of foo in their name field. It will be true for every class that inherits from Kit.

If must be overriden is a requirement then I suggest to add a constructor of Kit to force the user to add data from the base class.

public abstract class Kit {
    protected String name;

    public Kit (String name) {
        this.name = name;
    }

    public String getName () {
        return name;
    }
}

Now every class that inherits from Kit must invoke super (String), meaning the name field will be set for every object. It can be different from class A extends Kit and class B extends Kit. Is that what you searched for?

If so, then implementing class A and class B will look along these lines.

class A extends Kit {
    public A (String name) {
        super (name);
    }
}

And for B it will be the following.

class B extends Kit {
    public B (String name) {
        super (name);
    }
}

Now they are different classes, can hold different fields and methods, but they both need to set the name field of the base class: Kit.

0
Raffaele On

Static methods aren't virtual, which means you can't define an abstract static method on an interface and expect it to be overridden and called at the subclass level like:

// Doesn't even compile!
public interface Kit {
  static String name();
}

public class Ninja implements Kit {
  @Override public static name() { return "Ninja"; }
}

If you need to define values as runtime constants in the scope of a given class (for example all Ninja instances must return "NINJA"), you may hardcode the name at the class level in a regular, non-static, virtual method, or otherwise use something injection friendly like

public class KitTranslator {
  public void translate(Kit kit) {
    // ...
  }
}

You can then ensure there exists only one KitTranslator in your application.

I strongly discourage to use any implementation involving static, because it will mess with the initialization order of your application sooner or later, and because static fields propagates indefinetly until the userspace. Also, since static is not virtual at any level, you'll have troubles when you need to grow your code.

0
ZhongYu On

The easiest way is to require subclasses to contain some particular static fields

/**
 * subclass MUST define static field "KIT_NAME" !!!!
 */
public interface Kit

---

public class Ninja implements Kit

    public static final String KIT_NAME = "Ninja"

This requirement cannot be enforced by the compiler. It's a convention that the programmer must follow. That's not really a big problem.

At runtime, use reflection to read the field from the subclass.


Another, fancier way, is by using annotation

@KitInfo(name="Ninja")
public class Ninja implements Kit

This is a little better than static fields. You can even write a preprocessor to enforce that @KitInfo must be annotated for any subclass of Kit. (What can't be done by preprocessor?:)


Now, for the fun of it, let's solve it by generics

public interface Kit<I extends Kit.Info>
{
    public interface Info
    {
        String name();
    }
}

public class Ninja implements Kit<Ninja.Info>
{
    public static class Info implements Kit.Info
    { 
        public String name(){ return "Ninja"; }
    }
}

At runtime, given a subclass of Kit, we can use reflection to get the concrete type of I.