I theoretically understand the point why there is no abstract static in Java, as explained for instance in Why can't static methods be abstract in
annotations could be fine for your purpose.
@FileProperties(desc="data file")
public class DataFile extends XFile { ... }
FileProperties props = DataFile.class.getAnnotation(FileProperties.class);
String desc = props.desc();
Accessing the info still requires reflection, however it's a little better than using static field/method.
Java compiler does not enforce that all subclasses are annotated as such. You can add your logic to the compiler (using annotation processing) but that's too complicated. It's ok to check it at runtime.
Update:
This is also possible:
@FileInfoClass ( DataFileInfo.class )
@public class DataFile
The question is not clear enough to provide an objective answer. Since I cannot give you a fish, this answer is more on the lines of "Teach you to fish"
When faced with design issues like these where you think "duh..now sure why such a simple thing is so hard" more often than not, you are either designing it blatantly wrong or you are over complicating things. If I am empathizing correctly, the design issue seems like a "common requirement" yet the language is not allowing it.
...and you will most likely arrive at an acceptable answer.
If you still don't, post back the classes and interfaces you think you want (with compile errors since language is not allowing certain things) and maybe we can help you tune your design.
I don't know how a java guru would solve it, but I'd probably create a resource bundle with all the descriptions in a properties file like this:
com.bigcompany.smallapp.files.DataFile=Data file
com.bigcompany.smallapp.files.ConfigFile=Config file
Handling the bundle can conveniently be placed in the superclass or elsewhere.
Another option is to use reflection to access the static fields in each subclass, but then you need to make sure that all the subclasses have a static field with the same name.
There could be other options too, even refactoring the code so that the subtypes aren't represented by a separate class each, but in general there's no watertight solution.
I basically had the exact same problem.
You may want to look at the solutions suggested to me in my question
I liked Bozho's idea, but according to himself it was a bad idea. :) I suppose better programmers can explain why it is so. Ralph's and Jon Skeet's solution also works.
Instead of putting your static properties actually in static properties, put a reference to MyFileTypeDescription as a static property.
i.e.
class MyFileType {
static MyFileTypeDescription description;
...
<your regular attributes>
}
abstract class MyFileTypeDescription {
String name;
abstract String getDescription();
}
Something along this way, if I understood your problem correctly.
To restate the problem: you want your per-file-type classes to have statically available information on the type (e.g., name and description).
We can easily get part-way there: create a separate class for your type info, and have a static instance of this (appropriately instantiated) in each per-file-type class.
package myFileAPI;
public class TypeInfo {
public final String name;
public final String description;
public TypeInfo(String name, String description) {
this.name = name;
this.description = description;
}
}
and, say:
package myFileAPI;
public class TextFile {
public static final TypeInfo typeInfo
= new TypeInfo("Text", "Contains text.");
}
Then you can do stuff like:
System.out.println(TextFile.typeInfo.name);
(Of course, you could also use getters in TypeInfo
to encapsulate the underlying strings.)
However, as you said, what we really want is to enforce the existence of a particular signature static method in all your per-file-type classes at compile time, but the 'obvious' design path leads to requiring an abstract static method in a common superclass which isn't allowed.
We can enforce this at run-time though, which may be good enough to ensure it is coded correctly. We introduce a File superclass:
package myFileAPI;
public abstract class File {
public static TypeInfo getTypeInfo() {
throw new IllegalStateException(
"Type info hasn't been set up in the subclass");
}
}
If TextFile
now extends File
, we will get this exception when calling TextFile.getTypeInfo()
at runtime, unless TextFile has a same-signature method.
This is quite subtle: code with TextFile.getTypeInfo()
in still compiles, even when there is no such method in TextFile. Even though static methods are bound at compile time, the compiler can still look through the class hierarchy to determine the compile-time static call target.
So, we need code like:
package myFileAPI;
public class TextFile extends File {
private static final TypeInfo typeInfo
= new TypeInfo("Text", "Contains text.");
// Shadow the superclass static method
public static TypeInfo getTypeInfo() {
return typeInfo;
}
}
Note that we are still shadowing the superclass method, and so File.getTypeInfo()
can still be 'meaninglessly' called.