30

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 Java .

But how do I solve such a problem then?

My application uses files of a few types, which I want to assign static properties like a description of that file type (like "data file", the other being "config file", etc.). Obviously, I would put that into a static String so that the description is accessible without instancing a file (useful for the GUI f.i.). On the other hand, obviously all file types should have some common methods like getStatus(), which obviously I want to inherit from a common superclass MyFileType.

getDescription() would of course be abstract in the superclass.

Tried using a combination of a superclass and an interface, but similar problem: A static implementation of an abstract method is not allowed.

How would a Java guru solve this? Is it really such a bad implementation that I want to create?

Many thanks, Philipp

Community
  • 1
  • 1
Philippp
  • 795
  • 1
  • 6
  • 16

9 Answers9

35

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.

Stuart Rossiter
  • 1,726
  • 13
  • 19
  • 1
    The accepted answer, and others, still didn't really address the intuitive design requirement that type info would be _statically_ available from the classes. – Stuart Rossiter Nov 30 '12 at 13:34
  • The first part effectively tweaks templatetypedef's answer. By 'You could also associate a type with each actual file you use by just having it store a FileType reference.', I'm assuming he meant a _static_ reference as I have done, but just didn't make that clear. – Stuart Rossiter Nov 30 '12 at 14:54
  • monsieurRigsby, thank you very much - I think this is really the best answer to my problem! – Philippp Dec 01 '12 at 22:54
  • @Philippp Happy to help! (It was a nice problem to think about.) Maybe you could vote up the answer as well: it looks a bit weird having an accepted answer with zero votes. – Stuart Rossiter Jan 01 '13 at 17:02
  • 1
    So how would you access this property from a static method in the base class? E.g., suppose you wanted to implement `public static String getFancyDescription()` in `File`, so that calling `TextFile.getFancyDescription()` would return "Text: Contains text". Can this be done? – edam Mar 07 '17 at 09:24
  • Great answer! Thanks a lot! – Vall0n Feb 20 '19 at 14:13
7

This sounds like a great time to pull out the Fundamental Theorem of Software Engineering:

Any problem can be solved by adding another layer of indirection.

The problem you have right here is that a file carries around multiple pieces of information - what the type of the file is, a description of the file, the file contents, etc. I'd suggest splitting this into two classes - one class representing a concrete file on disk and its contents, and a second that is an abstract description of some file type. This would allow you to treat the file type class polymorphically. For example:

public interface FileType {
     String getExtension();
     String getDescription();

     /* ... etc. ... */
}

Now, you can make subclasses for each of the file types you use:

public class TextFileType implements FileType {
     public String getExtension() {
         return ".txt";
     }
     public String getDescription() {
         return "A plain ol' text file.";
     }
     /* ... */
}

You can then have some large repository of these sorts of objects, which would allow you to query their properties without having an open file of that type. You could also associate a type with each actual file you use by just having it store a FileType reference.

templatetypedef
  • 328,018
  • 92
  • 813
  • 992
  • 4
    Any problem can be solved by adding another layer of indirection. Even too many layers of indirection. :) – biziclop Jan 18 '11 at 23:30
  • Hi, thanks for that - indeed I was probably trying to do too much in one class :-) [ahhh, how do I get a new line in this input field?
    \n?] infact, your suggestion is what came to my mind when I woke up today - and I think I will take this approach, it looks very clear. thank you very much!
    – Philippp Jan 19 '11 at 21:16
  • 1
    Hm, but also with this approach, getDescription() cannot be static. Maybe this is an academic question, but somehow I felt it should be static because all my .txt file instances should of course have the same type description. I also thought about the description being generally available without instancing a file, though I probably wont need that. – Philippp Jan 20 '11 at 00:05
  • @Philippp Yes, I agree with you that the intuitive requirement for _statically_ accessible type info. is important. I've tried to incorporate that into my answer. I also think templatetypedef's answer has a level of indirection too far (unless you have more rich design requirements for your type info classes). – Stuart Rossiter Nov 30 '12 at 13:35
3

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
irreputable
  • 42,827
  • 9
  • 59
  • 89
  • Hi, thanks for this solution! Did not know such things are possible. It does exactly what I wanted to do, and I almost started to code. But I think I will prefer to use a separate abstract class for the filetype, because it would allow me to also put some static methods there. But thanks for opening my eyes on using Annotations! – Philippp Jan 19 '11 at 21:36
2

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 just incorrectly, or you are overcomplicating things. If I am empathizing correctly, your design issue seems like a "common requirement" yet the language is not allowing for any elegant solutions.

  • Trace back your design steps/decisions
  • question all the "obvious" and "of course" you are basing your design on (you are using quite a few above)
  • see if things can be simplified (don't take any of the OO concepts to their logical extreme. Make compromises based on ROI)

...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.

Amol Katdare
  • 6,484
  • 2
  • 30
  • 36
  • 2
    +1 Meta-answers aren't appreciated by many, but I like this approach. – biziclop Jan 18 '11 at 23:29
  • 1
    Well, I expected the teaching more than the fish itself, so it is perfectly ok :-) I was trying to do too much in one class - see my comment to templatetypedef above. Thanks for your thoughts! – Philippp Jan 19 '11 at 21:23
0

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.

Community
  • 1
  • 1
Tommy
  • 491
  • 1
  • 4
  • 4
-1

Sounds like you need to use a singleton. Basically, you call a static method like MyFileTypes.getDataFileInstance() which creates a single instance (or reuses if already created) of an object and when you first create it setup the 'constants' as needed. I'll see if I can find you a good example but your post isn't very clear about how you want to use it.

Abdullah Jibaly
  • 47,520
  • 35
  • 114
  • 192
  • Thanks for answer and inspiration! Yes a singleton should do the trick - though, its probably a bit oversized approach as long as I dont need variables, but just static texts for each of my file types. Still, thanks for letting me learn for the future! – Philippp Jan 19 '11 at 21:39
-1

You could create a FileMetadata class that has all the info you need. When your app starts up, you could create instances of FileMetadata, and keep static pointers to them so you can access them from anywhere in the JVM.

This way you put the abstract stuff in the actual instances; anything the stuff that does not call for abstract semantics can be static...

hvgotcodes
  • 109,621
  • 25
  • 195
  • 231
  • Yeah, thats a good idea! Its the next step after what came to my mind this morning, will see if I take it. Thanks very much! – Philippp Jan 19 '11 at 21:47
-1

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.

biziclop
  • 46,403
  • 12
  • 73
  • 97
  • Wow, the resource bundle certainly is a great thing. I am not sure if I will use it now, but I will definitely keep this in mind for when we start thinking about localization! Thanks very much! – Philippp Jan 19 '11 at 21:09
-1

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.

Jochen Bedersdorfer
  • 3,968
  • 23
  • 25
  • Now *I* am not sure if I understand correctly :-) . I will think about it again after some sleep... – Philippp Jan 19 '11 at 22:13
  • Hmmm, I dont think I understand. No matter how I put it, I eventually run against some static implementation of an interface or abstract class. Probably I would need two classes for each MyFileType: One actual file type, for instance MyFileText, and a file type class MyFileTypeText which contains the static description. In MyFileText I would then query MyFileTypeText.getDescription(). Not sure if thats what you mean, and it needs coding these queries in each MyFile subclass again. – Philippp Jan 20 '11 at 07:35