17

I would like to create a constant not implemented in super class in order to force subclasses to implement it. The best solution that I've found (on this topic) is to create an abstract method that will return the constant value. I assume that it is impossible to do something like:

abstract final static String Name;

But I still have hope because Java uses something like this in Serializable interface with the serialVersionUID. Did someone know how did they do this? Is it possible to reproduce it in my own class?

zb226
  • 9,586
  • 6
  • 49
  • 79
Julien Maret
  • 579
  • 1
  • 4
  • 22
  • the question is how is this solved with serialVersionUID? So it's possible somehow... – Philipp Sander Mar 19 '19 at 10:42
  • 4
    FYI, in the `Serializable` case it's the runtime that wants the `serialVersionUID`, not the compiler. A class that implements `Serializable` and doesn't have that field compiles just fine. – Federico klez Culloca Mar 19 '19 at 10:42
  • Also, `serialVersionUID` is not mandatory for the runtime, either. If it doesn't exist, the serialization mechanism calculates it. – RealSkeptic Mar 19 '19 at 10:47
  • Even if it is not mandatory, it still ask for it. The serialVersionIUD is not the exact solution that I want, but it come close to it. – Julien Maret Mar 19 '19 at 10:54
  • @JulienMaret are you more concerned with emitting a warning if it is not there or do you want to get a default value if it is not? Or both? – Captain Man Mar 19 '19 at 15:06
  • [What is the XY problem?](https://meta.stackexchange.com/a/66378/242352). There is no way to enforce it, as enforcing it would not have any actual consequences to the code. So why do you think you have to enforce it? – Holger Mar 19 '19 at 15:28

5 Answers5

12

Such constant cannot be static because static fields are shared among all instances of the class, including instances of all subclasses. Here is how to implement this as non-static constant:

public abstract class Foo {
  public final String name; // Particular value to be defined in subclass

  protected Foo (String name) {
    this.name = name;
  }
}

public class Bar extends Foo {
  public Bar () {
    super ("Zoo"); // Here we define particular value for the constant
  }
}

BTW, serialVersionUID is not a part of Serializable interface.

Mikhail Vladimirov
  • 13,572
  • 1
  • 38
  • 40
10

serialVersionUID field presence is not enforced by the Serializable interface because interface can't enforce presence of a field. You can declare a class which implements Serializable, it will compile just fine without serialVersionUID field being there.

The check for serialVersionUID field is hardcoded in the tools. One example is JDK java.io.ObjectStreamClass.getSerialVersionUID() methods that loads the serialVersionUID value with reflection:

/**
 * Returns explicit serial version UID value declared by given class, or
 * null if none.
 */
private static Long getDeclaredSUID(Class<?> cl) {
    try {
        Field f = cl.getDeclaredField("serialVersionUID");
        int mask = Modifier.STATIC | Modifier.FINAL;
        if ((f.getModifiers() & mask) == mask) {
            f.setAccessible(true);
            return Long.valueOf(f.getLong(null));
        }
    } catch (Exception ex) {
    }
    return null;
}
Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
2

I wouldn't recommend it but if you need it so much you could create a regex check in Checkstyle and force people to implement the static variable

Captain Man
  • 6,997
  • 6
  • 48
  • 74
ave4496
  • 2,950
  • 3
  • 21
  • 48
  • @JulienMaret Checkstyle is not just an Eclipse plugin. It can also be part of your build process like Maven. I don't think this is a good solution but just want to point out it is not actually IDE specific. – Captain Man Mar 19 '19 at 15:07
1

When a class implementing Serializable does not have a serialVersionUID the little message you see in your IDE and while compiling is a warning emitted by javac. If you want to create something like that you can but the process seems complicated. The solution is here in this answer.

They go into detail but the general idea is to create an annotation and annotation processor and use the annotation processor during compilation. I'm guessing you could use reflection (or... not reflection since it is compile time?) to see if the annotated class contains the field you want.

Captain Man
  • 6,997
  • 6
  • 48
  • 74
0

Here the best way I found to simulate it. Javadoc prevent prevent a bit to bad extends... but id subclass dont have NAME it will just fail to execute

public abstract class Foo {
    protected final String NAME;
    public Foo() {
        String name="";
        try {
            name = (String) this.getClass().getDeclaredField("NAME").get(name);
        } catch (NoSuchFieldException 
                 | SecurityException 
                 | IllegalArgumentException 
                 | IllegalAccessException e) {
            e.printStackTrace();
        }
        NAME = name;
    }
}

public class Bar extends Foo {
    public static final String NAME = "myName";
}
Julien Maret
  • 579
  • 1
  • 4
  • 22
  • Are you sure the code will not **compile**? Your code looks like it will not only compile, but also run without error since you catch the exception ? – Falco Mar 19 '19 at 14:28
  • I think you would want to set `NAME` to `Foo.NAME` in the catch block, right? That way if they don't "override" it it will use the "default"? – Captain Man Mar 19 '19 at 15:11
  • Why are you passing the empty `String` referenced by `name` to the `get` method declared by your (sub)class? That’s obviously never the right object for this operation. And when you have checked that `NAME` does exist, what do you gain from that? Are you aware that this code will break when you create a subclass of `Bar`? – Holger Mar 19 '19 at 16:06